package com.authine.cloudpivot.engine.service.impl.runtime;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.BooleanUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.TypeReference;
import com.authine.cloudpivot.engine.api.constants.Constants;
import com.authine.cloudpivot.engine.api.constants.ModelConstant;
import com.authine.cloudpivot.engine.api.exceptions.ServiceException;
import com.authine.cloudpivot.engine.api.facade.BizBusFacade;
import com.authine.cloudpivot.engine.api.facade.BizDataRuleFacade;
import com.authine.cloudpivot.engine.api.facade.DepartmentFacade;
import com.authine.cloudpivot.engine.api.facade.UserFacade;
import com.authine.cloudpivot.engine.api.model.ModelViewQueryModel;
import com.authine.cloudpivot.engine.api.model.bizmodel.BizDataRuleModel;
import com.authine.cloudpivot.engine.api.model.bizmodel.QuoteModel;
import com.authine.cloudpivot.engine.api.model.bizservice.ParameterModel;
import com.authine.cloudpivot.engine.api.model.organization.DepartmentModel;
import com.authine.cloudpivot.engine.api.model.organization.UserModel;
import com.authine.cloudpivot.engine.api.model.runtime.BizObjectModel;
import com.authine.cloudpivot.engine.api.model.runtime.SelectionValue;
import com.authine.cloudpivot.engine.component.query.api.*;
import com.authine.cloudpivot.engine.component.query.api.helper.*;
import com.authine.cloudpivot.engine.domain.bizmodel.BizProperty;
import com.authine.cloudpivot.engine.domain.bizmodel.Mapping;
import com.authine.cloudpivot.engine.domain.bizmodel.Quote;
import com.authine.cloudpivot.engine.domain.bizquery.BizQueryColumn;
import com.authine.cloudpivot.engine.domain.bizrule.BusinessRule;
import com.authine.cloudpivot.engine.domain.runtime.*;
import com.authine.cloudpivot.engine.enums.ErrCode;
import com.authine.cloudpivot.engine.enums.status.DataRowStatus;
import com.authine.cloudpivot.engine.enums.type.*;
import com.authine.cloudpivot.engine.enums.type.bizmodel.DataRuleType;
import com.authine.cloudpivot.engine.enums.type.bizrule.DataSourceType;
import com.authine.cloudpivot.engine.enums.type.bizrule.DefaultBusinessRuleType;
import com.authine.cloudpivot.engine.enums.type.bizrule.RuleNodeType;
import com.authine.cloudpivot.engine.service.bizmodel.BizPropertyService;
import com.authine.cloudpivot.engine.service.bizmodel.BizSchemaService;
import com.authine.cloudpivot.engine.service.bizquery.BizQueryColumnService;
import com.authine.cloudpivot.engine.service.bizrule.BusinessRuleService;
import com.authine.cloudpivot.engine.service.runtime.*;
import com.authine.cloudpivot.foundation.orm.api.Db;
import com.authine.cloudpivot.foundation.orm.api.model.BizObjectOptions;
import com.authine.cloudpivot.foundation.orm.api.model.BizObjectQueryObject;
import com.authine.cloudpivot.foundation.orm.api.model.QueryRelationObject;
import com.authine.cloudpivot.foundation.util.Lang;
import com.google.common.base.Joiner;
import com.google.common.base.Preconditions;
import com.google.common.base.Strings;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.MapUtils;
import org.apache.commons.lang3.ClassUtils;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.DateUtils;
import org.apache.commons.lang3.tuple.ImmutablePair;
import org.apache.commons.lang3.tuple.Pair;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Nullable;
import java.nio.charset.StandardCharsets;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

import static com.authine.cloudpivot.engine.service.impl.runtime.BizObjectServiceImplCompanion.*;
import static com.authine.cloudpivot.foundation.util.Lang.isIterable;

@Service
@Slf4j
public class BizObjectConverterImpl implements BizObjectConverter {

    private static final ImmutableSet<String> trueStringValues = ImmutableSet.of("true", "1");

    private final Logger logger = LoggerFactory.getLogger(this.getClass());

    @Autowired
    private BizAttachmentService bizAttachmentService;

    @Autowired
    private BizQueryColumnService bizQueryColumnService;

    @Autowired
    private UserFacade userFacade;

    @Autowired
    private DepartmentFacade departmentFacade;

    @Autowired
    private BizPropertyService bizPropertyService;

    @Autowired
    private BizObjectService bizObjectService;

    @Autowired
    private BusinessRuleService businessRuleService;

    @Autowired
    private BizDataRuleFacade bizDataRuleFacade;

    @Autowired
    private Db db;

    @Autowired
    private BizBusFacade bizServiceFacade;

    @Autowired
    private BizSchemaService bizSchemaService;

    @Autowired
    private BusinessRuleRuntimeService businessRuleRuntimeService;

    @Value("${cloudpivot.bizobject.view.tree.maxDepth:10}")
    private int maxDepth;

    private FilterExpression filterExpression;

    @Override
    public Map<String, Object> toArgs(final BizObject bizObject, final List<ParameterModel> parameters, @Nullable final List<Mapping> mappings) {
        final Map<String, Object> data = bizObject.getData();
//        if (data == null || data.isEmpty()) {
//            return Collections.emptyMap();
//        }

        if (mappings == null || mappings.isEmpty()) {
            return Collections.emptyMap();
        }

        final MapOps.Context context = new MapOps.Context(new MapOps.BizObjectStrategy(), data, bizServiceFacade);
        return MapOps.map(data, mappingWithTypes(parameters, mappings), MapOps.MapDir.TO_ARGS, context);
    }

    @Override
    public Map<String, Object> toArgs(final BizObjectQueryObject bizObjectQueryObject, final List<ParameterModel> parameters, @Nullable final List<Mapping> mappings) {
        Preconditions.checkNotNull(bizObjectQueryObject, "bizObjectQueryObject should not be null");
        if (mappings == null || mappings.isEmpty()) {
            return Collections.emptyMap();
        }

        final Map<String, Object> qoMap = queryObjectToSimpleMap(bizObjectQueryObject);

        logger.debug("query object map: {}", qoMap);
        final MapOps.Context context = new MapOps.Context(new MapOps.QueryBizObjectStrategy(), qoMap, bizServiceFacade);
        return MapOps.map(qoMap, mappingWithTypes(parameters, mappings), MapOps.MapDir.TO_ARGS, context);
    }

    private Map<String, Object> queryObjectToSimpleMap(final BizObjectQueryObject bizObjectQueryObject) {
        final Map<String, Object> qoMap = Maps.newHashMap();

        qoMap.put("schemaCode", bizObjectQueryObject.getSchemaCode());

        final Pageable pageable = bizObjectQueryObject.getPageable();
        if (pageable != null) {
            int offset = pageable.getStart();
            int limit = pageable.getLimit();
            int page = offset / limit;

            qoMap.put("offset", offset);
            qoMap.put("offset0", offset);
            qoMap.put("offset1", offset + 1);

            qoMap.put("limit", limit);

            qoMap.put("page", page);
            qoMap.put("page0", page);
            qoMap.put("page1", page + 1);
        }

        final Sortable sortable = bizObjectQueryObject.getSortable();
        if (sortable != null) {
            sortable.getOrders().stream().findFirst().ifPresent(o -> {
                qoMap.put("orderBy", o.getField());
                qoMap.put("orderDir", o.getDir().name());
            });
        }

        fillFilters(qoMap, bizObjectQueryObject.getFilterExpression());

        return qoMap;
    }

    /* rec */
    private void fillFilters(final /*mut*/Map<String, Object> qoMap, final FilterExpression filterExpr) {
        if (filterExpr instanceof FilterExpression.And) { // And, 只取第一级全部的Item
            FilterExpression.And andExpr = (FilterExpression.And) filterExpr;
            for (FilterExpression it : andExpr.items) {
                if (it instanceof FilterExpression.Item) {
                    fillFilterItem(qoMap, (FilterExpression.Item) it);
                }
                if (it instanceof FilterExpression.And) {
                    fillFilters(qoMap, it);
                }
                if (it instanceof FilterExpression.Or) {
                    List<FilterExpression> list = ((FilterExpression.Or) it).items;
                    List valveList = new ArrayList();
                    String key = "";
                    for (FilterExpression f : list) {
                        if (f instanceof FilterExpression.Item) {
                            valveList.add(((FilterExpression.Item) f).value);
                            key = ((FilterExpression.Item) f).field;
                        }
                    }
                    qoMap.put(key, valveList);
                }
            }
        } else if (filterExpr instanceof FilterExpression.Or) { // Or ， 只取包含一个Item的
            FilterExpression.Or orExpr = (FilterExpression.Or) filterExpr;
            List<FilterExpression> its = orExpr.items.stream().filter(it -> it != FilterExpression.empty).collect(Collectors.toList());
            if (its.size() == 1) {
                FilterExpression fe = its.get(0);
                if (fe instanceof FilterExpression.Item) {
                    fillFilterItem(qoMap, (FilterExpression.Item) fe);
                }
            }
        } else if (filterExpr instanceof FilterExpression.Item) {
            fillFilterItem(qoMap, (FilterExpression.Item) filterExpr);
        }
    }

    private void fillFilterItem(final /*mut*/Map<String, Object> qoMap, FilterExpression.Item item) {
        switch (item.op) {
            case Eq:
            case In:
            case Like:
                qoMap.put(item.field, item.value);
                break;
            case Gte:
                Object value = qoMap.get(item.field);
                if (value != null) {
                    String object = String.valueOf(value);
                    qoMap.put(item.field, item.value + ";_;" + object);
                    break;
                }
                qoMap.put(item.field, item.value);
                break;
            case Lte:
                value = qoMap.get(item.field);
                if (value != null) {
                    String object = String.valueOf(value);
                    qoMap.put(item.field, object + ";_;" + item.value);
                    break;
                }
                qoMap.put(item.field, item.value);
                break;
            default:
        }
    }

    @Override
    public Map<String, Object> fromSingleReturn(Object returnObject, List<ParameterModel> parameters, @Nullable List<Mapping> mappings) {
        if (returnObject == null) {
            return Collections.emptyMap();
        }

        if (mappings == null || mappings.isEmpty()) {
            return Collections.emptyMap();
        }

        final Map<String, Object> returnMap = MapOps.fromObject(returnObject);
        final MapOps.Context context = new MapOps.Context(new MapOps.BizObjectStrategy(), returnMap, bizServiceFacade);
        return MapOps.map(returnMap, mappingWithTypes(parameters, mappings), MapOps.MapDir.FROM_RETURN_OBJECT, context);
    }

    @Override
    @SuppressWarnings("unchecked")
    public Page fromListReturn(@Nullable Object returnObject, List<ParameterModel> parameters, @Nullable List<Mapping> mappings) {
        if (returnObject == null) {
            return new PageImpl(0, Collections.emptyList());
        }
        if (mappings == null || mappings.isEmpty()) {
            return new PageImpl(0, Collections.emptyList());
        }

        if (Lang.isIterable(returnObject)) {
            List list = Lang.asList(returnObject);
            return new PageImpl(list.size(), list);
        }

        if (ClassUtils.isPrimitiveOrWrapper(returnObject.getClass())) {
            return new PageImpl(0, Collections.emptyList());
        }

        return MapOps.toPage(returnObject, mappingWithTypes(parameters, mappings), bizServiceFacade);
    }

    @Override
    public Object fromFormValue(@Nullable final Object value, final BizProperty bizProperty) {
        if (value == null || StringUtils.isBlank(value.toString())) {
            return null;
        }
        final String code = bizProperty.getCode();
        switch (bizProperty.getPropertyType()) {
            case SHORT_TEXT:
            case RADIO:
                final String valueString = value.toString();
                final Integer maxLength = bizProperty.getPropertyLength();
                if (maxLength != null) {
                    if (valueString.length() > bizProperty.getPropertyLength()) {
                        final String msg = String.format("%s 值过长, 支持的最大长度: %d", code, bizProperty.getPropertyLength());
                        throw new EngineRuntimeException(ErrorCode.BAD_REQUEST, msg);
                    }
                }
                return valueString;

            case NUMERICAL:
                try {
                    Double.parseDouble(value.toString());
                } catch (Exception e) {
                    throw new EngineRuntimeException(ErrorCode.BAD_REQUEST, String.format("%s: %s为非数字型值", code, value));
                }
                return value;

            case LOGICAL:
                return fromFormLogicValue(value);

            case DATE:
                if (value instanceof Date) {
                    return value;
                } else {
                    try {
                        Long valueL;
                        if (value instanceof Long) {
                            valueL = (Long) value;
                            Date date = new Date(valueL);
                            SimpleDateFormat sdf = new SimpleDateFormat(supportsDatePatterns[0]);
                            return sdf.format(date);
                        }
                        return DateUtils.parseDate(value.toString(), supportsDatePatterns);
                    } catch (ParseException e) {
                        final String msg = String.format("%s: %s 日期格式不合法。当前支持格式: %s", code, value, Joiner.on(",").join(supportsDatePatterns));
                        throw new EngineRuntimeException(ErrorCode.BAD_REQUEST, msg);
                    }
                }

            case SELECTION: //选人/部门
            case STAFF_SELECTOR:
            case STAFF_MULTI_SELECTOR:
            case DEPARTMENT_SELECTOR:
            case DEPARTMENT_MULTI_SELECTOR:
                return fromFormSelectionValue(code, value);

            case WORK_SHEET:
                return fromFormWorkSheetValue(code, value);

            case MULT_WORK_SHEET:
                return fromFormWorkSheetValue(code, value);

            case ADDRESS:
                return fromFormAddressValue(code, value);

            default:
                return value;
        }
    }

    @Override
    public Object fromInputValue(@Nullable Object value, BizProperty bizProperty) {
        if (value == null || StringUtils.isBlank(value.toString())) {
            return null;
        }
        final String code = bizProperty.getCode();
        switch (bizProperty.getPropertyType()) {
            case SHORT_TEXT:
            case RADIO:
                final String valueString = value.toString();
                final Integer maxLength = bizProperty.getPropertyLength();
                if (maxLength != null) {
                    if (valueString.length() > bizProperty.getPropertyLength()) {
                        final String msg = String.format("%s 值过长, 支持的最大长度: %d", code, bizProperty.getPropertyLength());
                        throw new EngineRuntimeException(ErrorCode.BAD_REQUEST, msg);
                    }
                }
                return valueString;

            case NUMERICAL:
                double num;
                try {
                    num = Double.parseDouble(value.toString());
                } catch (Exception e) {
                    throw new EngineRuntimeException(ErrorCode.BAD_REQUEST, String.format("%s: %s为非数字型值", code, value));
                }
                return num;

            case LOGICAL:
                return fromFormLogicValue(value);

            case DATE:
                if (value instanceof Date) {
                    return value;
                } else {
                    try {
                        Long valueL;
                        if (value instanceof Long) {
                            valueL = (Long) value;
                            Date date = new Date(valueL);
                            SimpleDateFormat sdf = new SimpleDateFormat(supportsDatePatterns[0]);
                            return sdf.format(date);
                        }
                        return DateUtils.parseDate(value.toString(), supportsDatePatterns);
                    } catch (ParseException e) {
                        final String msg = String.format("%s: %s 日期格式不合法。当前支持格式: %s", code, value, Joiner.on(",").join(supportsDatePatterns));
                        throw new EngineRuntimeException(ErrorCode.BAD_REQUEST, msg);
                    }
                }

            case SELECTION: //选人/部门
            case STAFF_MULTI_SELECTOR:
            case DEPARTMENT_MULTI_SELECTOR:
                return dbFormatFromSelectors(code, value, bizProperty);
            case STAFF_SELECTOR:
            case DEPARTMENT_SELECTOR:
                return getIdFromObject(value);
            case WORK_SHEET:
                return fromFormWorkSheetValue(code, value);

            case MULT_WORK_SHEET:
                return fromFormWorkSheetValue(code, value);

            case ADDRESS:
                return fromFormAddressValue(code, value);

            default:
                return value;
        }
    }

    private String dbFormatFromSelectors(String code, Object value, BizProperty bizProperty) {
        String selectionValue = fromFormSelectionValue(code, value);
        if (StringUtils.isBlank(selectionValue)) {
            return null;
        }
        List<SelectionValue> selectionValueList = JSONObject.parseArray(selectionValue, SelectionValue.class);
        if (CollectionUtils.isEmpty(selectionValueList)) {
            return null;
        }
        if (bizProperty.getPropertyType() == BizPropertyType.SELECTION) {
            return selectionValueList.stream().map(item -> item.getId() + "_" + item.getType().getIndex()).collect(Collectors.joining(";"));
        }
        return selectionValueList.stream().map(SelectionValue::getId).collect(Collectors.joining(";"));
    }

    private String fromFormAddressValue(String code, Object value) {
        if (logger.isDebugEnabled()) {
            logger.debug("fromFormAddressValue = {} ", JSON.toJSONString(value));
        }
        if (value == null) {
            return null;
        }

        if (value instanceof String) {
            return value.toString();
        } else {
            return JSON.toJSONString(value);
        }
    }

    private Object fromFormWorkSheetValue(String code, Object value) {
        if (Objects.isNull(value)) {
            return StringUtils.EMPTY;
        }

        if (value instanceof String) {
            return value;
        }

        if (value instanceof List) {
            return ((List) value).size() == 0 ? StringUtils.EMPTY : value;
        }

        if (value instanceof Map) {
            return ((Map) value).get("id");
        }
        throw new EngineRuntimeException(ErrorCode.BAD_REQUEST, code + "不支持的关联表单值:" + value);
    }

    @Override
    public Boolean fromFormLogicValue(@Nullable Object value) {
        if (value == null) {
            return null;
        }

        if (value instanceof Boolean) {
            return (Boolean) value;
        }
        final String valueString = value.toString();
        return trueStringValues.contains(valueString.toLowerCase());
    }

    @SuppressWarnings("unchecked")
    @Override
    public String fromFormSelectionValue(String code, Object value) {
        // 系统通用字段: 用户和部门id， 只取对应的id值
        if (isSystemUserCode(code) || isSystemDeptCode(code)) {
            return getIdFromObject(value);
        } else { //普通选人控件值, 转换为如JSON: [{"id": "", "type": 1}]格式
            List<SelectionValue> selectionValues;
            if (value instanceof String) {
                selectionValues = selectionValueListFromJSON((String) value);
            } else if (isIterable(value)) {
                final List<Object> mapList = Lang.asList(value);
                //按id 去重
                final Map<String, SelectionValue> idMap = Maps.newLinkedHashMap();
                for (Object m : mapList) {
                    String id;
                    SelectionValue selectionValue;
                    if (m instanceof SelectionValue) {
                        selectionValue = ((SelectionValue) m);
                        id = selectionValue.getId();
                    } else if (m instanceof Map) {
                        id = (String) ((Map) m).get("id");
                        selectionValue = JSON.parseObject(JSON.toJSONString(m), SelectionValue.class);
                    } else {
                        throw new IllegalArgumentException("选人控件集合元素类型只支持SelectionValue和Map, 不支持" + (m == null ? "null" : m.getClass()));
                    }

                    if (Strings.isNullOrEmpty(id)) {
                        throw new IllegalArgumentException("选人控件集合元素类型id为空");
                    }
                    idMap.put(id, selectionValue);
                }
                selectionValues = Lists.newArrayList(idMap.values());
            } else {
                throw new EngineRuntimeException(ErrorCode.BAD_REQUEST, String.format("选人控件值%s: %s非JSON、数组或者集合类型", code, value));
            }

            for (SelectionValue value1 : selectionValues) { //清理不需要保存到数据库的字段
                Preconditions.checkArgument(!Strings.isNullOrEmpty(value1.getId()), code + "选人控件值id不能为空");
                Preconditions.checkNotNull(value1.getType(), code + "选人控件值type不能为空");

                value1.setName(null);
                value1.setImgUrl(null);
                value1.setFullDepartment(null);
            }

            return JSON.toJSONString(selectionValues);
        }
    }

    private List<SelectionValue> selectionValueListFromJSON(String jsonString) {
        String jsonStringTrim = jsonString.trim();
        if (jsonStringTrim.startsWith("[")) {
            return JSON.parseObject(jsonString, new TypeReference<List<SelectionValue>>() {
            });
        } else if (jsonStringTrim.startsWith("{")) {
            return Lists.newArrayList(JSON.parseObject(jsonStringTrim, SelectionValue.class));
        } else {
            throw new IllegalArgumentException("不支持字符串JSON格式:" + jsonString);
        }
    }


    @Override
    public List<Map<String, Object>> fromDbValues(final List<Map<String, Object>> dataList, final BizPropertyMap bpMap) {
        if (Objects.isNull(bpMap)) {
            return Lists.newArrayList();
        }
        final List<Map<String, Object>> ret = Lists.newArrayListWithExpectedSize(dataList.size());
        for (Map<String, Object> data : dataList) {
            ret.add(fromDbValues(data, bpMap));
        }
        return ret;
    }


    @Override
    public List<Map<String, Object>> fromDbIgoValues(final List<Map<String, Object>> dataList, final BizPropertyMap bpMap) {
        if (Objects.isNull(bpMap)) {
            return Lists.newArrayList();
        }
        final List<Map<String, Object>> ret = Lists.newArrayListWithExpectedSize(dataList.size());
        for (Map<String, Object> data : dataList) {
            ret.add(fromDbIgoValues(data, bpMap));
        }
        return ret;
    }

    @Override
    public Map<String, Object> fromDbValues(final Map<String, Object> data, final BizPropertyMap bpMap) {
        return fromDbValues(data, bpMap, true);
    }

    @Override
    public Map<String, Object> fromDbValues(final Map<String, Object> data, final BizPropertyMap bpMap, boolean needDisplayField) {
        final Map<String, Object> ret = Maps.newHashMapWithExpectedSize(data.size());
        for (Map.Entry<String, Object> it : data.entrySet()) {
            final String code = it.getKey();
            final BizProperty bizProperty = bpMap.getIgnoreCase(code);
            if (bizProperty == null) {
                log.error("[bo query]数据项不存在 code={} value={}", code, it.getValue());
                continue;
//                throw new ServiceException(ErrCode.BIZ_PROPERTY_NOT_EXIST.getErrCode(), String.format("数据项:%s不存在", code));
            }
            final Object value = fromDbValue(it.getValue(), bizProperty, needDisplayField);
            ret.put(bizProperty.getCode(), value);
        }
        return ret;
    }

    @Override
    public Map<String, Object> fromDbIgoValues(Map<String, Object> data, BizPropertyMap bpMap) {
        final Map<String, Object> ret = Maps.newHashMapWithExpectedSize(data.size());
        for (Map.Entry<String, Object> it : data.entrySet()) {
            final String code = it.getKey();
            final BizProperty bizProperty = bpMap.getIgnoreCase(code);
            if (bizProperty == null) {
                continue;
            }
            final Object value = fromDbValue(it.getValue(), bizProperty, false);
            ret.put(bizProperty.getCode(), value);
        }
        return ret;
    }

    public List<SelectionValue> fromQuoteSelectionValue(@Nullable String code, @Nullable Object value) {
        if (value == null) {
            return Lists.newArrayList();
        }

        Preconditions.checkArgument(value instanceof String);
        final String valueString = (String) value;

        //系统选人选部门字段(存id）特殊优先处理
        if (code != null && isSystemUserCode(code)) {
            return Lists.newArrayList(idToSelectionValue(valueString, UnitType.USER, this::fillUserNameAndImageById));
        }
        if (code != null && isSystemDeptCode(code)) {
            return Lists.newArrayList(idToSelectionValue(valueString, UnitType.DEPARTMENT, this::fillDeptNameById));
        }

        final String valueJSON = (String) value;
        List<SelectionValue> selectionValues = new ArrayList<>();
        try {
            selectionValues = JSON.parseObject(valueJSON, new TypeReference<List<SelectionValue>>() {
            });

        } catch (Exception e) {
            logger.error("需要做兼容处理");
            SelectionValue selectionValue = new SelectionValue();
            UserModel user = userFacade.get(valueJSON);
            selectionValue.setId(valueJSON);
            if (user != null) {
                selectionValue.setType(UnitType.USER);
            } else {
                selectionValue.setType(UnitType.DEPARTMENT);
            }
            selectionValues.add(selectionValue);
        }

        for (SelectionValue selectionValue : selectionValues) {
            if (selectionValue.getType() == UnitType.USER) {
                fillUserNameAndImageById(selectionValue.getId(), selectionValue);
            }
            if (selectionValue.getType() == UnitType.DEPARTMENT) {
                fillDeptNameById(selectionValue.getId(), selectionValue);
            }
        }
        return selectionValues;
    }


    @Override
    public List<SelectionValue> fromDbSelectionValue(@Nullable String code, @Nullable Object value,
                                                     BizPropertyType bizPropertyType) {
        if (ObjectUtils.isEmpty(value)) {
            return Lists.newArrayList();
        }

        Preconditions.checkArgument(value instanceof String);
        final String valueString = (String) value;

        //系统选人选部门字段(存id）特殊优先处理
        if (code != null && isSystemUserCode(code)) {
            return Lists.newArrayList(idToSelectionValue(valueString, UnitType.USER, this::fillUserNameAndImageById));
        }
        if (code != null && isSystemDeptCode(code)) {
            return Lists.newArrayList(idToSelectionValue(valueString, UnitType.DEPARTMENT, this::fillDeptNameById));
        }

        if (bizPropertyType == BizPropertyType.STAFF_SELECTOR && !JSONUtil.isJson(valueString)) {
            return Lists.newArrayList(idToSelectionValue(valueString, UnitType.USER, this::fillUserNameAndImageById));
        }

        if (bizPropertyType == BizPropertyType.DEPARTMENT_SELECTOR && !JSONUtil.isJson(valueString)) {
            return Lists.newArrayList(idToSelectionValue(valueString, UnitType.DEPARTMENT, this::fillDeptNameById));
        }

        if (bizPropertyType == BizPropertyType.STAFF_MULTI_SELECTOR && !JSONUtil.isJson(valueString)) {
            List<String> idList = Arrays.asList(valueString.split(";"));
            return idList.stream().map(id -> idToSelectionValue(id, UnitType.USER, this::fillUserNameAndImageById)).collect(Collectors.toList());
        }

        if (bizPropertyType == BizPropertyType.DEPARTMENT_MULTI_SELECTOR && !JSONUtil.isJson(valueString)) {
            List<String> idList = Arrays.asList(valueString.split(";"));
            return idList.stream().map(id -> idToSelectionValue(id, UnitType.DEPARTMENT, this::fillDeptNameById)).collect(Collectors.toList());
        }

        if (bizPropertyType == BizPropertyType.SELECTION && !JSONUtil.isJson(valueString)) {
            List<String> idTypeList = Arrays.asList(valueString.split(";"));
            List<SelectionValue> result = new ArrayList<>(idTypeList.size());
            idTypeList.forEach(idType -> {
                String[] str = idType.split("_");
                if (str.length != 2) {
                    return;
                }
                if (StringUtils.equals(str[1], String.valueOf(UnitType.DEPARTMENT.getIndex()))) {
                    result.add(idToSelectionValue(str[0], UnitType.DEPARTMENT, this::fillDeptNameById));
                    return;
                }
                result.add(idToSelectionValue(str[0], UnitType.USER, this::fillUserNameAndImageById));
            });
            return result;
        }

        final String valueJSON = (String) value;
        List<SelectionValue> selectionValues = JSON.parseObject(valueJSON, new TypeReference<List<SelectionValue>>() {
        });
        if (CollectionUtils.isEmpty(selectionValues)) {
            return Lists.newArrayList();
        }

        for (SelectionValue selectionValue : selectionValues) {
            if (selectionValue.getType() == UnitType.USER) {
                fillUserNameAndImageById(selectionValue.getId(), selectionValue);
            }
            if (selectionValue.getType() == UnitType.DEPARTMENT) {
                fillDeptNameById(selectionValue.getId(), selectionValue);
            }
        }
        return selectionValues;
    }

    @Override
    public List<SelectionValue> getUserSelections(List<String> userIds) {

        if (CollectionUtils.isEmpty(userIds)) {
            return null;
        }
        List<UserModel> userModels = userFacade.listByIdList(new ArrayList<>(userIds));
        List<SelectionValue> selectionValues = Lists.newArrayListWithExpectedSize(userModels.size());
        userModels.forEach(it -> {
            selectionValues.add(
                    SelectionValue.builder()
                            .id(it.getId())
                            .name(it.getName())
                            .type(it.getUnitType())
                            .departmentId(it.getDepartmentId())
                            .imgUrl(it.getImgUrl())
                            .fullDepartment(getFullDeptName(it.getDepartmentId()))
                            .build());
        });
        return selectionValues;

    }

    @Override
    public Object fromQuoteValue(@Nullable final Object value, final BizProperty bizProperty) {
        final String code = bizProperty.getCode();
        final BizPropertyType bizPropertyType = bizProperty.getPropertyType();
        if (logger.isTraceEnabled()) {
            logger.trace("value: {}, bizProperty.code: {}, bizProperty.type: {}", JSON.toJSONString(value), code, bizPropertyType);
        }
        if (value == null) {
            return null;
        }

        switch (bizPropertyType) {
            case SELECTION:
            case STAFF_SELECTOR:
            case STAFF_MULTI_SELECTOR:
            case DEPARTMENT_SELECTOR:
            case DEPARTMENT_MULTI_SELECTOR:
                return fromQuoteSelectionValue(code, value);

            case LOGICAL:
                return fromDbLogicalValue(value);

            case ADDRESS:
                return fromDbAddressValue(value);

            case WORK_SHEET:
                return fromDbWorkSheetValue(value, bizProperty);

            default:
                return value;
        }
    }


    @Override
    public Object fromDbValue(@Nullable final Object value, final BizProperty bizProperty) {
        return fromDbValue(value, bizProperty, true);
    }

    public Object fromDbValue(@Nullable final Object value, final BizProperty bizProperty, boolean needDisplayField) {
        final String code = bizProperty.getCode();
        final BizPropertyType bizPropertyType = bizProperty.getPropertyType();
        if (logger.isTraceEnabled()) {
            logger.trace("value: {}, bizProperty.code: {}, bizProperty.type: {}", JSON.toJSONString(value), code, bizPropertyType);
        }
        if (value == null) {
            return null;
        }

        switch (bizPropertyType) {
            case SELECTION:
            case STAFF_SELECTOR:
            case STAFF_MULTI_SELECTOR:
            case DEPARTMENT_SELECTOR:
            case DEPARTMENT_MULTI_SELECTOR:
                return fromDbSelectionValue(code, value, bizPropertyType);

            case WORK_SHEET:
                return buildWorkSheetValue(value, bizProperty, needDisplayField);
            case MULT_WORK_SHEET:
                return fromDbMultWorkSheetValue(value, bizProperty, needDisplayField);
            case LOGICAL:
                return fromDbLogicalValue(value);

            case ADDRESS:
                return fromDbAddressValue(value);

            default:
                return value;
        }
    }

    @Override
    public List<Map<String, Object>> fromDbChildTableValue(String code, String objectId, boolean withResources) {
        final String childSchemaCode = code;
        List<BizProperty> bizProperties = bizPropertyService.getListBySchemaCode(childSchemaCode, true);
        final String sortColumn = DefaultSubPropertyType.SORT_KEY.getCode();
        //todo final List<Map<String, Object>> childTableData = iTableDmlSupport.find(new BizObjectQueryObject() {
        final List<Map<String, Object>> childTableData = db.dml().find(new BizObjectQueryObject() {
            @Override
            public String getSchemaCode() {
                return childSchemaCode;
            }

            @Override
            public FilterExpression getFilterExpression() {
                return new FilterExpression.Item(OBJECT_ID, FilterExpression.Op.Eq, objectId);
            }

            @Override
            public Sortable getSortable() {
                return new SortableImpl(Collections.singletonList(new OrderImpl(sortColumn, Order.Dir.ASC)));
            }
        }, bizProperties);
        final BizPropertyMap childBpMap = getBizPropertyMap(childSchemaCode);

        final /*mut*/ List<Map<String, Object>> converts = fromDbValues(childTableData, childBpMap);
        if (withResources) {
            try {
                final Map<BizPropertyType, List<BizProperty>> subGrouped = childBpMap.getValues().stream().collect(Collectors.groupingBy(BizProperty::getPropertyType));
                final List<BizProperty> childResourceProps = subGrouped.get(BizPropertyType.ATTACHMENT);
                if (childResourceProps != null) {
                    logger.debug("加载子表的资源数据...");

                    for (Map<String, Object> convert : converts) {
                        for (BizProperty childBizProperty : childResourceProps) {
                            final String childBizPropertyCode = childBizProperty.getCode();
                            final String childObjectId = (String) convert.get(ID);

                            convert.put(childBizPropertyCode, fromDbAttachments(childSchemaCode, childObjectId, childBizPropertyCode));
                        }
                    }
                }
            } catch (Exception e) {
                logger.error("fill child table resources error", e);
            }
        }

        return converts;
    }

    @Override
    public List<Map<String, Object>> fromDbChildTableValue(String code, String objectId) {
        List<BizProperty> bizProperties = bizPropertyService.getListBySchemaCode(code, true);
        //todo final List<Map<String, Object>> childTableData = iTableDmlSupport.find(new BizObjectQueryObject() {
        final List<Map<String, Object>> childTableData = db.dml().find(new BizObjectQueryObject() {
            @Override
            public String getSchemaCode() {
                return code;
            }

            @Override
            public FilterExpression getFilterExpression() {
                return new FilterExpression.Item(OBJECT_ID, FilterExpression.Op.Eq, objectId);
            }

            @Override
            public Sortable getSortable() {
                return new SortableImpl(Collections.singletonList(new OrderImpl(DefaultSubPropertyType.SORT_KEY.getCode(), Order.Dir.ASC)));
            }
        }, bizProperties);
        //todo final BizPropertyMap childBpMap = getBizPropertyMap(code);
        final BizPropertyMap childBpMap = new BizPropertyMap(bizProperties);
//        Map<String, Map<String, Object>> result = new HashMap<>();
//        for (Map<String, Object> data : childTableData) {
//            Map<String, Object> temp = fromDbValues(data, childBpMap);
//            result.put(temp.get(ID).toString(), temp);
//        }
        return this.fromDbValues(childTableData, childBpMap);

    }

    @Override
    public List<Map<String, Object>> findChildTableData(String code, BizObjectQueryObject bizObjectQueryObject) {
        List<BizProperty> bizProperties = bizPropertyService.getListBySchemaCode(code, true);
        final List<Map<String, Object>> childTableData = db.dml().find(bizObjectQueryObject, bizProperties);
        return childTableData;
    }

    @Override
    public Pair<List<Map<String, Object>>, Map<String, Map<String, Object>>> fromDbChildTableValue(String code, List<String> objectIds, boolean childPageEnable, int pageSize) {

        List<BizProperty> bizProperties = bizPropertyService.getListBySchemaCode(code, true);
        List<BizQueryColumn> columns = bizQueryColumnService.getListBySchemaCode(code);
        List<String > displayFields = Lists.newArrayList();
        for (BizQueryColumn bizQueryColumn : columns) {
            if (bizQueryColumn.getPropertyType().equals(BizPropertyType.WORK_SHEET) || bizQueryColumn.getPropertyType().equals(BizPropertyType.MULT_WORK_SHEET)) {
                if (bizQueryColumn.getVisible()) {
                    displayFields.add(bizQueryColumn.getPropertyCode());
                }
            } else {
                displayFields.add(bizQueryColumn.getPropertyCode());
            }
        }
        // List<String> displayFields = columns.stream().filter(column -> column.getVisible() && column.getPropertyType().equals(BizPropertyType.WORK_SHEET)).map(BizQueryColumn::getPropertyCode).collect(Collectors.toList());
        displayFields = completeChildSystemFields(code,null, displayFields);
        List<String> finalDisplayFields = displayFields;

        if (CollectionUtils.isEmpty(objectIds)) {
            return Pair.of(Collections.emptyList(), null);
        }
        List<Map<String, Object>> childTableData = Lists.newArrayList();
        Map<String, Map<String, Object>> childPageInfo = null;

        List<BizProperty> finalBizProperties;
        if (CollectionUtils.isNotEmpty(objectIds) && objectIds.size() > 1) {
            finalBizProperties = Lists.newArrayList();
            // 列表只显示子表勾选显示的字段
            for (BizProperty property : bizProperties) {
                if (displayFields.contains(property.getCode())) {
                    finalBizProperties.add(property);
                }
            }
        } else {
            // 说明是load接口
            finalBizProperties = bizProperties;
        }

        if (childPageEnable) {
            if (pageSize < 1) {
                pageSize = 10;
            }
            childPageInfo = Maps.newHashMapWithExpectedSize(objectIds.size());
            PageableImpl pageable = new PageableImpl(0, pageSize);
            for (String objectId : objectIds) {
                BizObjectQueryObject bizObjectQueryObject = new BizObjectQueryObject() {
                    @Override
                    public String getSchemaCode() {
                        return code;
                    }
                    @Override
                    public List<String> getDisplayFields() {
                        return finalDisplayFields;
                    }
                    @Override
                    public FilterExpression getFilterExpression() {
                        return new FilterExpression.Item(OBJECT_ID, FilterExpression.Op.Eq, objectId);
                    }

                    @Override
                    public Pageable getPageable() {
                        return pageable;
                    }

                    @Override
                    public Sortable getSortable() {
                        return new SortableImpl(Collections.singletonList(new OrderImpl(DefaultSubPropertyType.SORT_KEY.getCode(), Order.Dir.ASC)));
                    }
                };
                long total = db.dml().count(bizObjectQueryObject, finalBizProperties);
                Map<String, Object> map = Maps.newHashMapWithExpectedSize(3);
                map.put(ModelConstant.CHILD_PAGE_INFO_TOTAL_KEY, total);
                map.put(ModelConstant.CHILD_PAGE_INFO_SIZE_KEY, pageSize);
                map.put(ModelConstant.CHILD_PAGE_INFO_PAGE_KEY, total == 0 ? 0 : (total - 1) / pageSize + 1);
                childPageInfo.put(ModelConstant.CHILD_PAGE_INFO_KEY.concat(objectId), map);
                if (total == 0) {
                    break;
                }
                List<Map<String, Object>> data = db.dml().find(bizObjectQueryObject, finalBizProperties);
                if (data != null) {
                    childTableData.addAll(data);
                }
            }


        } else {
            Lists.partition(objectIds, getDbQueryPerMaxNum()).forEach(ids -> {
                List<Map<String, Object>> data = db.dml().find(new BizObjectQueryObject() {
                    @Override
                    public String getSchemaCode() {
                        return code;
                    }

                    @Override
                    public FilterExpression getFilterExpression() {
                        return new FilterExpression.Item(OBJECT_ID, FilterExpression.Op.In, ids);
                    }

                    @Override
                    public Sortable getSortable() {
                        return new SortableImpl(Collections.singletonList(new OrderImpl(DefaultSubPropertyType.SORT_KEY.getCode(), Order.Dir.ASC)));
                    }
                }, finalBizProperties);
                if (CollectionUtils.isNotEmpty(data)) {
                    childTableData.addAll(data);
                }
            });
        }
        List<Map<String, Object>> childDataList = formatDbData(childTableData, finalBizProperties);
        return Pair.of(new ArrayList<>(childDataList), childPageInfo);
    }

    @Override
    public List<Map<String, Object>> formatDbData(List<Map<String, Object>> data, List<BizProperty> bizProperties) {
        List<BizProperty> workFields = new ArrayList<>();
        if (CollectionUtils.isNotEmpty(bizProperties)) {
            List<BizProperty> workSheetProperty = bizProperties.stream().filter(property -> property.getPropertyType() == BizPropertyType.WORK_SHEET || property.getPropertyType() == BizPropertyType.MULT_WORK_SHEET).collect(Collectors.toList());
            workFields.addAll(workSheetProperty);
        }
        //去掉关联表单数据项
        final BizPropertyMap bizPropertiesMap = new BizPropertyMap(bizProperties);
        List<Map<String, Object>> dataList = this.fromDbIgoValues(data, bizPropertiesMap);
        boolean isEmpty = CollectionUtils.isEmpty(workFields);
        if (isEmpty || CollectionUtils.isEmpty(dataList)) {
            return dataList;
        }
        final Map<String, Map<String, Object>> dataMap = dataList.stream().collect(Collectors.toMap(m -> m.get(ID).toString(), Function.identity()));
        if (MapUtils.isEmpty(dataMap)) {
            return dataList;
        }
        for (BizProperty bizProperty : workFields) {
            String field = bizProperty.getCode();
            String schemaCode = bizProperty.getRelativeCode();
            //获取关联表单有数据的  id集合列表

            Set<String> workSheetIds = Sets.newHashSetWithExpectedSize(data.size());
            data.forEach(m -> {
                String id = (String) m.get(field);
                if (StringUtils.isBlank(id)) {
                    return;
                }
                if (id.indexOf(ModelConstant.MULTI_WORK_SHEET_SPILT.charAt(0)) > 0) {
                    workSheetIds.addAll(Arrays.asList(id.split(ModelConstant.MULTI_WORK_SHEET_SPILT)));
                } else {
                    workSheetIds.add(id);
                }
            });

            if (CollectionUtils.isEmpty(workSheetIds)) {
                dataMap.keySet().forEach(key -> dataMap.get(key).put(field, null));
                continue;
            }
            List<BizProperty> properties = bizPropertyService.getListBySchemaCode(schemaCode, true);
            List<String> propertyCodes = properties.stream().map(BizProperty::getCode).collect(Collectors.toList());
            String displayFieldCode = StringUtils.isEmpty(bizProperty.getRelativePropertyCode()) ? "name" : ((propertyCodes.contains(bizProperty.getRelativePropertyCode())) ? bizProperty.getRelativePropertyCode() : "name");
            BizProperty property = bizPropertyService.get(schemaCode, displayFieldCode);
            BizPropertyType propertyType = property.getPropertyType();
            //关联表单数据
            List<Map<String, Object>> workSheetValues = queryWorkSheetData(schemaCode, new ArrayList<>(workSheetIds), displayFieldCode, propertyType);
            // 以子表id为key  关联表单id为value
//            Map<String, String> idMap = data.stream().filter(m -> (m.get(field) != null) && (m.get(ID) != null)).collect(Collectors.toMap(m -> (m.get(ID).toString()), m -> (m.get(field).toString())));
            if (CollectionUtils.isEmpty(workSheetValues)) {
                continue;
            }
            //关联表单数据list列表转换为map集合  以id为key  id对应的数据行为value
            final Map<String, Map<String, Object>> workSheetMap = workSheetValues.stream().collect(Collectors.toMap(m -> m.get(ID).toString(), Function.identity(), (k1, k2) -> k1));
            //关联表单组装到子表上
            buildWorkSheetData(dataMap, workSheetMap, field, bizProperty.getPropertyType(), displayFieldCode);
        }
        return dataList;
    }

    private void buildWorkSheetData(Map<String, Map<String, Object>> data, Map<String, Map<String, Object>> workSheetMap,
                                    String field, BizPropertyType bizPropertyType, String displayFieldCode) {
        data.forEach((k, v) -> {
            if ((v.get(field) != null) && (v.get(ID) != null)) {
                String relationId = (String) ((Map) v.get(field)).get(ID);
                Map<String, Object> workSheetItem = new HashMap<>();
                //处理关联多选
                if (BizPropertyType.MULT_WORK_SHEET == bizPropertyType) {
                    String[] relationIds = relationId.split(ModelConstant.MULTI_WORK_SHEET_SPILT);
                    List<Map<String, Object>> items = Lists.newArrayListWithExpectedSize(relationIds.length);
                    for (String id : relationIds) {
                        Map<String, Object> item = Maps.newHashMapWithExpectedSize(2);
                        items.add(item);
                        item.put("id", id);
                        Map<String, Object> workSheetItemTemp = workSheetMap.get(id);
                        if (MapUtils.isEmpty(workSheetItemTemp)) {
                            continue;
                        }
                        if (workSheetItem.isEmpty()) {
                            workSheetItem.putAll(workSheetItemTemp);
                            workSheetItem.remove(displayFieldCode);
                        }
                        if (workSheetItemTemp.get(displayFieldCode) != null) {
                            item.put(displayFieldCode, buildWorkSheetDisplayValue(workSheetItemTemp.get(displayFieldCode), (BizPropertyType) workSheetItemTemp.get("propertyType")));
                        } else {
                            item.put(displayFieldCode, null);
                        }
                    }
                    if (!workSheetItem.isEmpty()) {
                        workSheetItem.put(ID, relationId);
                    }
                    workSheetItem.put("items", items);
                } else {
                    if (workSheetMap.get(relationId) != null) {
                        workSheetItem.putAll(workSheetMap.get(relationId));
                    }
                }
                if (workSheetItem.isEmpty() || !workSheetItem.containsKey("schemaCode")) {
                    v.put(field, null);
                } else {
                    v.put(field, workSheetItem);
                }
            }
        });
    }

    /**
     * api : 处理不同模型编码的引用   ----级联
     */
    @Override
    public Map<String, List<Quote>> parseQuote(String schemaCode, String workSheetCode, String relativePropertyCode) {
        final List<BizProperty> properties = bizPropertyService.getListBySchemaCode(schemaCode, true);
        if (CollectionUtils.isEmpty(properties)) {
            throw new ServiceException(ErrCode.BIZ_PROPERTY_NOT_EXIST.getErrCode(), ErrCode.BIZ_PROPERTY_NOT_EXIST.getErrMsg(), "数据项为空");
        }
        List<BizProperty> workSheets = properties.stream().filter(propertyModel -> propertyModel.getPropertyType() == BizPropertyType.WORK_SHEET).collect(Collectors.toList());
        if (CollectionUtils.isEmpty(workSheets)) {
            return Maps.newHashMap();
        }
        Map<String, List<Quote>> map = new HashMap<>();
        for (BizProperty bizPropertyModel : workSheets) {
            List<Quote> quoteCodes = bizPropertyModel.getQuoteCodes();
            if (CollectionUtils.isEmpty(quoteCodes)) {
                continue;
            }
            for (Quote quoteModel : quoteCodes) {
                List<Quote> list = new ArrayList<>();
                if (quoteModel.getCode().equals(relativePropertyCode)) {
                    quoteModel.setRelativePropertyCode(bizPropertyModel.getCode());
                    list.add(quoteModel);
                    parseQuoteModel(quoteModel, list);
                    map.put(workSheetCode, list);
                    break;
                }
            }
        }
        return map;
    }

    @Override
    public Map<String, Object> fromQuoteWorkSheetValue(Object value, BizProperty bizProperty) {
        return fromDbWorkSheetValue(value, bizProperty);
    }

    /**
     * api : 计算每一个引用数据项所有的引用   并保持引用全过程
     */
    private List<Quote> parseQuoteModel(Quote quoteModel, List<Quote> result) {
        String relativeCode = quoteModel.getRelativeCode();
        String schemaCode = quoteModel.getSchemaCode();
        final List<BizProperty> properties = bizPropertyService.getListBySchemaCode(schemaCode, true);
        if (CollectionUtils.isEmpty(properties)) {
            throw new ServiceException(ErrCode.BIZ_PROPERTY_NOT_EXIST.getErrCode(), ErrCode.BIZ_PROPERTY_NOT_EXIST.getErrMsg(), "数据项为空");
        }
        List<BizProperty> workSheets = properties.stream().filter(propertyModel -> propertyModel.getPropertyType() == BizPropertyType.WORK_SHEET).collect(Collectors.toList());
        if (CollectionUtils.isEmpty(workSheets)) {
            result.add(quoteModel);
            return result;
        }
        for (BizProperty bizPropertyModel : workSheets) {
            List<Quote> quoteCodes = bizPropertyModel.getQuoteCodes();
            if (CollectionUtils.isEmpty(quoteCodes)) {
                continue;
            }
            List<Quote> quotes = quoteCodes.stream().filter(quote -> Objects.equals(quote.getCode(), relativeCode)).collect(Collectors.toList());
            if (CollectionUtils.isEmpty(quotes)) {
                quoteModel.setRelativePropertyCode(bizPropertyModel.getCode());
                result.add(quoteModel);
                return result;
            }
            quotes.get(0).setRelativePropertyCode(bizPropertyModel.getCode());
            parseQuoteModel(quotes.get(0), result);
        }
        return result;
    }

    @Override
    public List<Map<String, Object>> fromDbChildTableValues(String code, boolean withResources) {
        final String childSchemaCode = code;

        final String sortColumn = DefaultSubPropertyType.SORT_KEY.getCode();
        //todo final List<Map<String, Object>> childTableData = iTableDmlSupport.find(new BizObjectQueryObject() {
        List<BizProperty> bizProperties = bizPropertyService.getListBySchemaCode(childSchemaCode, true);
        final List<Map<String, Object>> childTableData = db.dml().find(new BizObjectQueryObject() {
            @Override
            public String getSchemaCode() {
                return childSchemaCode;
            }

            //            @Override
//            public FilterExpression getFilterExpression() {
//                return new FilterExpression.Item(null,null,null);
//            }
            @Override
            public Sortable getSortable() {
                return new SortableImpl(Collections.singletonList(new OrderImpl(sortColumn, Order.Dir.ASC)));
            }
        }, bizProperties);
        //final BizPropertyMap childBpMap = getBizPropertyMap(childSchemaCode);
        final BizPropertyMap childBpMap = new BizPropertyMap(bizProperties);
        final /*mut*/ List<Map<String, Object>> converts = fromDbValues(childTableData, childBpMap);
        if (withResources) {
            try {
                final Map<BizPropertyType, List<BizProperty>> subGrouped = childBpMap.getValues().stream().collect(Collectors.groupingBy(BizProperty::getPropertyType));
                final List<BizProperty> childResourceProps = subGrouped.get(BizPropertyType.ATTACHMENT);
                if (childResourceProps != null) {
                    logger.debug("加载子表的资源数据...");

                    for (Map<String, Object> convert : converts) {
                        for (BizProperty childBizProperty : childResourceProps) {
                            final String childBizPropertyCode = childBizProperty.getCode();
                            final String childObjectId = (String) convert.get(ID);

                            convert.put(childBizPropertyCode, fromDbAttachments(childSchemaCode, childObjectId, childBizPropertyCode));
                        }
                    }
                }
            } catch (Exception e) {
                logger.error("fill child table resources error", e);
            }
        }

        return converts;
    }

    private BizPropertyMap getBizPropertyMap(String schemaCode) {
        final List<BizProperty> bpList = bizPropertyService.getListBySchemaCode(schemaCode, true);
        if (bpList.isEmpty()) {
            throw new ServiceException(ErrCode.BIZ_SCHEMA_NO_PROPERTY.getErrCode(), String.format(ErrCode.BIZ_SCHEMA_NO_PROPERTY.getErrMsg(), schemaCode), schemaCode);
        }

        return new BizPropertyMap(bpList);
    }

    private String fromDbAddressValue(Object value) {
        if (value == null) {
            logger.debug("fromDbAddressValue, value is null. ");
            return null;
        }

        if (value instanceof byte[]) {
            byte[] bytes = (byte[]) value;
            String valueString = new String(bytes, StandardCharsets.UTF_8);

            if (logger.isDebugEnabled()) {
                logger.debug("fromDbAddressValue = {} ", valueString);
            }
            return valueString;

        } else {
            return value.toString();
        }
    }

    private Boolean fromDbLogicalValue(@Nullable Object value) {
        if (value == null) {
            return null;
        }

        if (value instanceof Boolean) {
            return (Boolean) value;
        }

        String valueString = value.toString();
        return trueStringValues.contains(valueString.toLowerCase());
    }

    private Map<String, Object> buildWorkSheetValue(final Object value, final BizProperty bizProperty, boolean needDisplayField) {
        // 返回格式如: {"value": value, "name":name, "schemaCode": schemaCode}
        final String schemaCode = bizProperty.getRelativeCode();
        Preconditions.checkState(StringUtils.isNotEmpty(schemaCode), "关联的业务模型编码为空");
        final Map<String, Object> ret = Maps.newHashMap();
        BizProperty relativeBizProperty = getWorkSheetDisplayProperty(bizProperty);
        String displayCode = relativeBizProperty.getCode();
        ret.put("id", value);
        ret.put("schemaCode", schemaCode);
        ret.put("displayCode", displayCode);
        ret.put("propertyType", relativeBizProperty.getPropertyType());
        if (needDisplayField) {
            BizObject bizObject = queryRelationData(schemaCode, String.valueOf(value));
            if (bizObject != null) {
                ret.put(displayCode, bizObject.getData().get(displayCode));
            }
        }
        return ret;
    }

    private BizProperty getWorkSheetDisplayProperty(BizProperty bizProperty) {
        String schemaCode = bizProperty.getRelativeCode();
        BizProperty relativeBizProperty = null;
        String relativePropertyCode = bizProperty.getRelativePropertyCode();
        if (StringUtils.isNotBlank(relativePropertyCode)) {
            relativeBizProperty = bizPropertyService.get(schemaCode, relativePropertyCode);
            if (relativeBizProperty == null) {
                relativePropertyCode = null;
            }
        }
        if (StringUtils.isBlank(relativePropertyCode)) {
            relativePropertyCode = "name";
            relativeBizProperty = bizPropertyService.get(schemaCode, relativePropertyCode);
        }
        return relativeBizProperty;
    }

    private String buildWorkSheetDisplayValue(Object fromDbValue, BizPropertyType relativeBizPropertyType) {
        String fromDbValueStr = null;
        switch (relativeBizPropertyType) {
            case DATE:
                if (Objects.nonNull(fromDbValue)) {
                    fromDbValueStr = com.authine.cloudpivot.engine.utils.DateUtils.getDate((Date) fromDbValue, "yyyy-MM-dd HH:mm:ss");
                }
                break;
            case SELECTION:
            case STAFF_SELECTOR:
            case STAFF_MULTI_SELECTOR:
            case DEPARTMENT_SELECTOR:
            case DEPARTMENT_MULTI_SELECTOR:
                if (Objects.nonNull(fromDbValue)) {
                    List<SelectionValue> selectionValues = (List<SelectionValue>) fromDbValue;
                    if (CollectionUtil.isNotEmpty(selectionValues)) {
                        fromDbValueStr = StringUtils.join(selectionValues.stream().map(SelectionValue::getName).collect(Collectors.toList()), ",");
                    }
                }
                break;
            case LOGICAL:
                if (Objects.nonNull(fromDbValue)) {
                    fromDbValueStr = "true".equalsIgnoreCase(fromDbValue.toString()) ? "是" : "否";
                }
                break;
            case ADDRESS:
                String json = null;
                if (fromDbValue != null) {
                    json = fromDbValue.toString();
                }
                if ("{}".equals(json)) {
                    json = null;
                }
                if (StringUtils.isNotEmpty(json)) {
                    Map map = JSONObject.parseObject(json, Map.class);
                    String name = "";
                    if (map.get("provinceName") != null) {
                        name = name + map.get("provinceName");
                    }
                    if (map.get("cityName") != null) {
                        name = name + map.get("cityName");
                    }
                    if (map.get("districtName") != null) {
                        name = name + map.get("districtName");
                    }
                    if (map.get("address") != null) {
                        name = name + map.get("address");
                    }
                    fromDbValueStr = name;
                }
                break;
            default:
                break;

        }
        if (StringUtils.isBlank(fromDbValueStr)) {
            if (Objects.nonNull(fromDbValue)) {
                fromDbValueStr = fromDbValue.toString();
            }
        }
        return fromDbValueStr;
    }

    private Map<String, Object> fromDbWorkSheetValue(final Object value, final BizProperty bizProperty) {
        // 返回格式如: {"value": value, "name":name, "schemaCode": schemaCode}
        final String schemaCode = bizProperty.getRelativeCode();
        Preconditions.checkState(StringUtils.isNotEmpty(schemaCode), "关联的业务模型编码为空");

        final Map<String, Object> ret = Maps.newHashMap();
        ret.put("id", value);
        ret.put("schemaCode", schemaCode);

        final String relTable = db.ddl().getTableName(schemaCode);
        String relativePropertyCode = bizProperty.getRelativePropertyCode();
        if (StringUtils.isEmpty(relativePropertyCode)) {
            relativePropertyCode = "name";
        } else {
            BizProperty relativeProperty = bizPropertyService.get(schemaCode, relativePropertyCode);
            if (relativeProperty == null) {
                relativePropertyCode = "name";
            }
        }
        final String displayCode = relativePropertyCode;
        ret.put("displayCode", displayCode);
        BizProperty relativeBizProperty = bizPropertyService.get(schemaCode, displayCode);
        ret.put("propertyType", relativeBizProperty.getPropertyType());
        try {

            Object fromDbValue = getObject(value, schemaCode, displayCode, relativeBizProperty);
            ret.put(displayCode, fromDbValue);
        } catch (Exception e) {
            logger.warn("getFieldById on {} error: {}", relTable, e.getMessage());

            //修复bug，关联表单数据displayCode为空
            String dc = bizRuleWorkSheetValue(schemaCode, value.toString(), displayCode);
            ret.put(displayCode, dc);
        }

        return ret;
    }

    private String bizRuleWorkSheetValue(final String schemaCode, final String objectId, String displayCode) {
        try {
            BizObjectVO bizObject = new BizObjectVO(schemaCode, objectId, Maps.newHashMap());
            bizObject.getData().put("id", objectId);
            Boolean businessRuleEnable = bizSchemaService.getBySchemaCode(schemaCode).getBusinessRuleEnable();
            if (businessRuleEnable) {
                BizObject bj = businessRuleRuntimeService.loadBO(bizObject, null);
                if (bj != null && bj.getData() != null) {
                    Object object = bj.getData().get(displayCode);
                    if (object != null) {
                        return object.toString();
                    }
                }
            }
            return null;
        } catch (Exception e) {
            logger.warn("bizRuleWorkSheetValue on schemaCode={},objectId={} error: {}", schemaCode, objectId, e.getMessage());
            return null;
        }
    }


    @SuppressWarnings("unchecked")
    private Map<String, Object> fromDbMultWorkSheetValue(final Object value, final BizProperty bizProperty, boolean needDisplayField) {
        // 返回格式如: {"value": value, "name":name, "schemaCode": schemaCode}
        final String schemaCode = bizProperty.getRelativeCode();
        Preconditions.checkState(StringUtils.isNotEmpty(schemaCode), "关联的业务模型编码为空");

        final Map<String, Object> ret = Maps.newHashMap();
        BizProperty relativeBizProperty = getWorkSheetDisplayProperty(bizProperty);
        String displayCode = relativeBizProperty.getCode();
        ret.put("id", value);
        ret.put("schemaCode", schemaCode);
        ret.put("displayCode", displayCode);
        ret.put("propertyType", relativeBizProperty.getPropertyType());

        if (needDisplayField) {
            final String relTable = db.ddl().getTableName(schemaCode);
            try {
                String[] split = String.valueOf(value).split(ModelConstant.MULTI_WORK_SHEET_SPILT);
                List<Map<String, Object>> items = Lists.newArrayListWithExpectedSize(split.length);
                for (int i = 0; i < split.length; i++) {
                    Map<String, Object> item = Maps.newHashMapWithExpectedSize(2);
                    items.add(item);
                    item.put("id", split[i]);
                    BizObject bizObject = queryRelationData(schemaCode, split[i]);
                    if (bizObject == null || bizObject.getData().size() <= 1) {
                        continue;
                    }
                    Object displayValue = bizObject.getData().get(displayCode);
                    if (displayValue != null) {
                        item.put(displayCode, buildWorkSheetDisplayValue(displayValue, relativeBizProperty.getPropertyType()));
                    } else {
                        item.put(displayCode, null);
                    }
                }
                ret.put("items", items);
            } catch (Exception e) {
                logger.warn("getFieldById on {} error: {}", relTable, e.getMessage());
                ret.put("items", null);
            }
        }

        return ret;
    }

    private Object getObject(Object value, String schemaCode, String displayCode, BizProperty relativeBizProperty) {
        //todo Object propertyValue = iTableDmlSupport.findOneConditionNoLob(new BizObjectQueryObject() {
        List<BizProperty> bizProperties = bizPropertyService.getListBySchemaCode(schemaCode, true);
        Object propertyValue = db.dml().findOne(new BizObjectQueryObject() {
            @Override
            public String getSchemaCode() {
                return schemaCode;
            }

            @Override
            public FilterExpression getFilterExpression() {
                return new FilterExpression.Item(ID, FilterExpression.Op.Eq, value);
            }

            @Override
            public List<String> getDisplayFields() {
                return Collections.singletonList(displayCode);
            }
        }, bizProperties, Object.class);
        return fromDbValue(propertyValue, relativeBizProperty);
    }

    @Override
    @Transactional(readOnly = true)
    public List<Comment> fromDbComments(List<BizComment> bizComments) {
        final BizObjectConverterImpl self = this;
        return bizComments.stream().map(c -> {
            final List<BizAttachment> bizAttachments = bizAttachmentService.findResources(c.getTableName(), c.getId(), c.getBizPropertyCode());
            return new Comment() {
                @Override
                public String getId() {
                    return c.getId();
                }

                @Override
                public String getSchemaCode() {
                    return c.getSchemaCode();
                }

                @Override
                public String getBizObjectId() {
                    return c.getBizObjectId();
                }

                @Override
                public String getBizPropertyCode() {
                    return c.getBizPropertyCode();
                }

                @Override
                public String getActivityCode() {
                    return c.getActivityCode();
                }

                @Override
                public String getActivityName() {
                    return c.getActivityName();
                }

                @Override
                public String getWorkflowInstanceId() {
                    return c.getWorkflowInstanceId();
                }

                @Override
                public String getWorkflowTokenId() {
                    return c.getWorkflowTokenId();
                }

                @Nullable
                @Override
                public String getWorkItemId() {
                    return c.getWorkItemId();
                }

                @Override
                public Integer getTokenId() {
                    return c.getTokenId();
                }

                @Override
                public ActionType getActionType() {
                    return c.getActionType();
                }

                @Override
                public FormActionType getResult() {
                    return c.getResult();
                }

                @Override
                public String getContent() {
                    return c.getContent();
                }

                @Override
                public SelectionValue getCreater() {
                    return idToSelectionValue(c.getCreater(), UnitType.USER, self::fillUserNameAndImageById);
                }

                @Override
                public SelectionValue getCreaterDept() {
                    UserModel createUser = userFacade.get(c.getCreater());
                    if (createUser != null) {
                        return idToSelectionValue(createUser.getDepartmentId(), UnitType.DEPARTMENT, self::fillDeptNameById);
                    }
                    return null;
                }

                @Override
                public SelectionValue getModifier() {
                    String modifier = c.getModifier();
                    if (!Strings.isNullOrEmpty(modifier)) {
                        return idToSelectionValue(modifier, UnitType.USER, self::fillUserNameAndImageById);
                    }
                    return null;
                }

                @Override
                public SelectionValue getModifierDept() {
                    String modifier = c.getModifier();
                    if (Strings.isNullOrEmpty(modifier)) {
                        return null;
                    }
                    UserModel modifierUser = userFacade.get(c.getModifier());
                    if (modifierUser != null) {
                        return idToSelectionValue(modifierUser.getDepartmentId(), UnitType.DEPARTMENT, self::fillDeptNameById);
                    }
                    return null;
                }

                @Override
                public Date getCreatedTime() {
                    return c.getCreatedTime();
                }

                @Override
                public Date getModifiedTime() {
                    return c.getModifiedTime();
                }

                @Override
                public List<SelectionValue> getRelUsers() {
                    String relUsers = c.getRelUsers();
                    if (!Strings.isNullOrEmpty(relUsers)) {
                        return fromDbSelectionValue(null, relUsers, BizPropertyType.SELECTION);
                    }
                    return Collections.emptyList();
                }

                @Override
                public List<? extends Attachment> getAttachments() {
                    return bizAttachments;
                }
            };
        }).collect(Collectors.toList());
    }

    @Override
    public List<Map<String, Object>> fromDbAttachments(List<BizAttachment> bizAttachments) {
        final List<Map<String, Object>> ret = Lists.newArrayListWithExpectedSize(bizAttachments.size());
        for (BizAttachment bizAttachment : bizAttachments) {
            Map<String, Object> m = Maps.newHashMap();
            m.put("id", bizAttachment.getId());
            m.put("refId", bizAttachment.getRefId());
            m.put("name", bizAttachment.getName());
            m.put("fileSize", bizAttachment.getFileSize());
            m.put("fileExtension", bizAttachment.getFileExtension());
            m.put("mimeType", bizAttachment.getMimeType());
            m.put("base64ImageStr", bizAttachment.getBase64ImageStr());
            m.put("creater", idToSelectionValue(bizAttachment.getCreater(), UnitType.USER, this::fillUserNameAndImageById));
            m.put("createdTime", bizAttachment.getCreatedTime());
            ret.add(m);
        }
        return ret;
    }

    @Override
    public SelectionValue getIdToSelectionValue(String creater) {
        return idToSelectionValue(creater, UnitType.USER, this::fillUserNameAndImageById);
    }


    @Override
    public List<Map<String, Object>> fromDbAttachments(String schemaCode, String objectId, String propertyCode) {
        final List<BizAttachment> bizAttachments = bizAttachmentService.findResources(schemaCode, objectId, propertyCode);
        return fromDbAttachments(bizAttachments);
    }

    private SelectionValue fillUserNameAndImageById(final String id, final /*mut*/SelectionValue selectionValue) {
        if (StringUtils.isBlank(id)) {
            return selectionValue;
        }
        UserModel user = userFacade.get(id);
        if (user == null) {
            logger.warn("get user null by id:{}", id);
            return selectionValue;
        }
        selectionValue.setName(user.getName());
        selectionValue.setImgUrl(user.getImgUrl());
        selectionValue.setFullDepartment(getFullDeptName(user.getDepartmentId()));
        return selectionValue;
    }

    private SelectionValue fillDeptNameById(final String id, final /*mut*/SelectionValue selectionValue) {
        if (StringUtils.isBlank(id)) {
            logger.warn("id is blank:{}", id);
            return selectionValue;
        }

        DepartmentModel dept = departmentFacade.get(id);
        if (dept == null) {
            logger.warn("get department null by id:{}", id);
            return selectionValue;
        }
        String name = dept.getName();
        selectionValue.setName(name);
        if (!StringUtils.equalsAny(dept.getId(), Constants.EXTERNAL_DEPARTMENT, Constants.MANAGEMENT_DEPARTMENT)) {
            selectionValue.setFullDepartment(getFullDeptName(id));
        }
        return selectionValue;
    }

    public List<ParameterModel> transferParameters(List<ParameterModel> origin, String prefixBiz) {
        List<ParameterModel> result = Lists.newArrayListWithExpectedSize(origin.size());
        if (StringUtils.isNotEmpty(prefixBiz)) {
            prefixBiz = prefixBiz + ".";
        }
        for (ParameterModel m : origin) {
            ExternParameterType type = m.getExternParameterType();
            if (type == ExternParameterType.OBJECT) {
                result.addAll(transferParameters(m.getSubParameters(), prefixBiz + m.getCode()));
            } else if (type == ExternParameterType.LIST) {
                String newCode = prefixBiz + m.getCode();
                m.setCode(newCode);
                m.setBizPropertyType(BizPropertyType.CHILD_TABLE);
                result.add(m);
                result.addAll(transferParameters(m.getSubParameters(), newCode));
            } else {
                String newCode = prefixBiz + m.getCode();
                m.setCode(newCode);
                result.add(m);
            }
        }
        return result;
    }

    private List<ImmutablePair<Mapping, BizPropertyType>> mappingWithTypes(final List<ParameterModel> parameters, final List<Mapping> mappings) {
        List<ParameterModel> newParameters = transferParameters(parameters, "");
        final Map<String/*parameterCode*/, BizPropertyType> parameterPropertyTypeMap = newParameters.stream().collect(
                HashMap::new, (map, item) -> map.put(item.getCode(), item.getBizPropertyType()), HashMap::putAll);
        return mappings.stream().map(it -> ImmutablePair.of(it, parameterPropertyTypeMap.get(it.getServiceMethodParameterCode()))).collect(Collectors.toList());
    }

    //倒序输出当前组织的全路径组织
    private String getFullDeptName(String deptmentId) {
        List<String> deptNames = new ArrayList<>();
        getParentDeptNames(deptNames, deptmentId);
        if (CollectionUtils.isNotEmpty(deptNames)) {
            //倒序列
            Collections.reverse(deptNames);
            return StringUtils.join(deptNames, "/");
        }
        return "";
    }

    private void getParentDeptNames(List<String> list, String parentDeptId) {
        if (StringUtils.isBlank(parentDeptId)) {
            return;
        }

        DepartmentModel dept = departmentFacade.get(parentDeptId);
        if (dept == null) {
            return;
        }

        if (StringUtils.equalsAny(dept.getId(), Constants.EXTERNAL_DEPARTMENT, Constants.MANAGEMENT_DEPARTMENT)) {
            return;
        }

        list.add(dept.getName());
        //递归上一级部门
        this.getParentDeptNames(list, dept.getParentId());
    }

    /**
     * 将数据库查询结果转换为数据项类型结果
     * @param dataList
     * @param queryRelationObject
     * @param bizPropertyMap
     * @return
     */
    @Override
    public List<Map<String, Object>> fromDbValues(List<Map<String, Object>> dataList, QueryRelationObject queryRelationObject, Map<String, BizProperty> bizPropertyMap) {
        if (CollectionUtils.isEmpty(queryRelationObject.getDisplayFields()) || CollectionUtils.isEmpty(dataList) || MapUtils.isEmpty(bizPropertyMap)) {
            return dataList;
        }
        List<Map<String, Object>> result = new ArrayList<>(dataList.size());
        dataList.forEach(data -> {
            Map<String, Object> ret = Maps.newHashMapWithExpectedSize(data.size());
            for (Map.Entry<String, Object> it : data.entrySet()) {
                String code = it.getKey();
                BizProperty bizProperty = bizPropertyMap.get(code);
                if (bizProperty == null) {
                    ret.put(code, it.getValue());
                    continue;
                }
                Object value = fromDbValue(it.getValue(), bizProperty);
                ret.put(code, value);
            }
            result.add(ret);
        });
        return result;
    }

    @Override
    public List<Map<String, Object>> queryWorkSheetData(String schemaCode, List<String> objectIds, String displayFieldCode, BizPropertyType propertyType) {

        List<Map<String, Object>> workSheetData = new ArrayList<>();

        if (businessRuleIsBizAction(schemaCode, DefaultBusinessRuleType.LOAD)) {
            // GetList业务规则数据源为集成时 走单个查询
            objectIds.forEach(it -> {
                BizObject bizObject = queryRelationData(schemaCode, it);
                if (bizObject != null && bizObject.getData() != null) {
                    workSheetData.add(bizObject.getData());
                }
            });

        } else {
            List<String> fields = new ArrayList<>();
            fields.add(displayFieldCode);
            fields.add(ID);
            BizObjectQueryObject bizObjectQueryObject = new BizObjectQueryObject() {
                @Override
                public String getSchemaCode() {
                    return schemaCode;
                }

                @Override
                public List<String> getDisplayFields() {
                    return fields;
                }

                @Override
                public FilterExpression getFilterExpression() {
                    return Q.it(ID, FilterExpression.Op.In, objectIds);
                }

                @Override
                public Pageable getPageable() {
                    return new PageableImpl(0, objectIds.size());
                }
            };

            Page<Map<String, Object>> data = bizObjectService.queryBizObjects(bizObjectQueryObject);

            if (Objects.isNull(data) || CollectionUtils.isEmpty(data.getContent())) {
                return Collections.emptyList();
            }
            workSheetData.addAll(data.getContent());
        }

        workSheetData.forEach(map -> {
            map.put("propertyType", propertyType);
            map.put("schemaCode", schemaCode);
            map.put("displayCode", displayFieldCode);
        });

        return workSheetData;
    }

    /**
     * in分批查询，oracle 1000，其他数据库2000
     * @return int
     **/
    @Override
    public int getDbQueryPerMaxNum() {
        return db.dml().getDbQueryPerMaxNum();
    }

    /**
     * 处理主表关联单选
     * @param workSheetFields  关联单选、关联多选字段
     * @param bpMap            数据项
     * @param mainTableDataMap 主表字段
     * @return void
     **/
    @Override
    public void dealMainWorkSheets(Set<String> workSheetFields, BizPropertyMap bpMap, Map<String, Map<String, Object>> mainTableDataMap) {

        Map<String, Object> fieldEmplyMap = new HashMap<>();
        for (String field : workSheetFields) {
            BizProperty biz = bpMap.getIgnoreCase(field);
            String relativeCode = biz.getRelativeCode();
            Set<String> ids = new HashSet<>();
            mainTableDataMap.forEach((k, v) -> {
                if (ObjectUtil.isNotEmpty(v.get(field))) {
                    String id = ((Map) v.get(field)).get(ID).toString();
                    if (BizPropertyType.MULT_WORK_SHEET == biz.getPropertyType()) {
                        //关联多选
                        ids.addAll(Arrays.asList(id.split(ModelConstant.MULTI_WORK_SHEET_SPILT)));
                    } else {
                        ids.add(id);
                    }
                }
            });

            if (CollectionUtils.isEmpty(ids)) {
                fieldEmplyMap.put(field, null);
                continue;
            }

            //关联单选、多选的展示字段
            BizProperty relativeDisplayProperty = getRelativeDisplayProperty(relativeCode, biz.getRelativePropertyCode());
            String displayFieldCode = relativeDisplayProperty.getCode();

            List<String> objectIds = new ArrayList<>(ids);
            List<Map<String, Object>> workSheetData = new ArrayList<>();
            //分批in查询，oracle 1000，其他数据库2000
            Lists.partition(objectIds, getDbQueryPerMaxNum()).forEach(idsTmp -> {
                workSheetData.addAll(queryWorkSheetData(relativeCode, idsTmp, displayFieldCode, relativeDisplayProperty.getPropertyType()));
            });

            if (CollectionUtils.isEmpty(workSheetData)) {
                mainTableDataMap.keySet().forEach(key -> mainTableDataMap.get(key).put(field, null));
                continue;
            }

            //关联表单数据从list转换为map形式  以id为key，id对应的值为value
            final Map<String, Map<String, Object>> workSheetMap = workSheetData.stream().collect(Collectors.toMap(m -> m.get(ID).toString(), Function.identity()));
            //组装到主表
            buildWorkSheetData(mainTableDataMap, workSheetMap, field, biz.getPropertyType(), displayFieldCode);

            if (!fieldEmplyMap.isEmpty()) {
                mainTableDataMap.forEach((k, v) -> v.putAll(fieldEmplyMap));
            }

        }
    }

    private BizProperty getRelativeDisplayProperty(String schemaCode, String relativePropertyCode) {
        List<BizProperty> properties = bizPropertyService.getListBySchemaCode(schemaCode, true);
        String displayFieldCode = StringUtils.isEmpty(relativePropertyCode) ? "name" : ((properties.stream().anyMatch(it -> it.getCode().equals(relativePropertyCode))) ? relativePropertyCode : "name");
        return properties.stream().filter(it -> it.getCode().equals(displayFieldCode)).findAny().get();
    }

    @Override
    public Object dealBizMethodData(Object bizMethodResult, String schemaCode) {
        List<BizProperty> bizProperties = bizPropertyService.getListBySchemaCode(schemaCode, true);
        BizPropertyMap bizPropertyMap = new BizPropertyMap(bizProperties);
        if (bizMethodResult instanceof Page) {
            Page page = (Page) bizMethodResult;
            if (page != null && CollectionUtils.isNotEmpty(page.getContent())) {
                List<Map<String, Object>> dataList = this.fromDbIgoValues(page.getContent(), new BizPropertyMap(bizProperties));
                Set<String> workSheetFields = bizProperties.stream().filter(it -> BizPropertyType.WORK_SHEET == it.getPropertyType() || BizPropertyType.MULT_WORK_SHEET == it.getPropertyType())
                        .map(BizProperty::getCode).collect(Collectors.toSet());
                final Map<String, Map<String, Object>> mainTableDataMap = dataList.stream().filter(it -> it.get(ID) != null).collect(Collectors.toMap(m -> m.get(ID).toString(), Function.identity()));
                dealMainWorkSheets(workSheetFields, bizPropertyMap, mainTableDataMap);
                page.getContent().clear();
                page.getContent().addAll(dataList);
            }
        } else if (bizMethodResult instanceof Map) {
            bizMethodResult = this.fromDbValues((Map) bizMethodResult, bizPropertyMap);
        }
        return bizMethodResult;
    }

    @Override
    public boolean businessRuleIsBizAction(String schemaCode, DefaultBusinessRuleType businessRuleType) {
        BusinessRule businessRule = businessRuleService.getBySchemaCodeAndCode(schemaCode, businessRuleType.getCode());
        if (businessRule != null && JSONUtil.isJsonArray(businessRule.getNode())) {
            JSONArray nodeJsonArray = JSONUtil.parseArray(businessRule.getNode());
            return nodeJsonArray.stream().anyMatch(it -> {
                cn.hutool.json.JSONObject node = JSONUtil.parseObj(it);
                switch (businessRuleType) {
                    case LOAD:
                        return RuleNodeType.BIZ_ACTION.name().equals(node.getStr("nodeType")) && !BooleanUtil.isTrue((Boolean) node.get("disable"));
                    case GET_LIST:
                        return DataSourceType.BIZ_SERVICE.name().equals(node.getStr("dataSourceType")) && !BooleanUtil.isTrue((Boolean) node.get("disable"));
                    default:
                        // TODO UPDATE、DELETE
                        return false;
                }
            });
        }
        return false;
    }

    @Override
    public Map<Boolean, Set<String>> checkDataExist(String schemaCode, List<String> ids) {
        List<BizProperty> bizProperties = bizPropertyService.getListBySchemaCode(schemaCode, true);
        Set<String> existIds = new HashSet<>();
        Lists.partition(ids, getDbQueryPerMaxNum()).forEach(tempIds -> {
            final List<Map<String, Object>> data = db.dml().find(new BizObjectQueryObject() {

                @Override
                public String getSchemaCode() {
                    return schemaCode;
                }

                @Override
                public FilterExpression getFilterExpression() {
                    return new FilterExpression.Item(ID, FilterExpression.Op.In, tempIds);
                }

                @Override
                public List<String> getDisplayFields() {
                    return Collections.singletonList(ID);
                }
            }, bizProperties);

            if (CollectionUtils.isNotEmpty(data)) {
                List<String> dbIds = data.stream().filter(it -> it.get(ID) != null).map(k -> (String) k.get(ID)).collect(Collectors.toList());
                existIds.addAll(new HashSet<>(dbIds));
            }
        });
        Map<Boolean, Set<String>> map = new HashMap<>();
        if (existIds.isEmpty()) {
            map.put(false, new HashSet<>(ids));
            map.put(true, Collections.emptySet());
        } else {
            map.put(true, existIds);
            map.put(false, (Set) CollUtil.subtract(new HashSet<>(ids), existIds));
        }
        return map;
    }

    @Override
    public BizObject queryRelationData(String schemaCode, String objectId) {
        BizObjectOptions options = BizObjectOptions.builder().notQueryRelationColumn().notQueryChild().build();
        return bizObjectService.loadBizObject(schemaCode, objectId, null, options);
    }

    @Override
    public int getTreeMaxDepth() {
        return maxDepth;
    }

    @Override
    public List<Map<String, Object>> queryTreeData(BizObjectQueryObject bizObjectQueryObject, List<Map<String, Object>> content,
                                                   List<BizProperty> bizProperties, String parentRefFieldCode) {

        if (bizObjectQueryObject.getViewType() == null) {
            return null;
        }
        if (!bizObjectQueryObject.getViewType().isTreeModel()) {
            return null;
        }

        // 最大深度 默认20
        Integer depthObj = bizObjectQueryObject.getViewType().getDepth();
        int depth = depthObj == null ? 1 : (depthObj == 0 ? maxDepth : Integer.min(depthObj, maxDepth));

        // 深度大于5 总数据1000内 直接查询所有数据再处理 TODO 全量加载性能问题
        List<Map<String, Object>> allData = null;
        long total = db.dml().count((BizObjectQueryObject) bizObjectQueryObject::getSchemaCode, bizProperties);
        if (depth > 3 && total < 1000) {
            if (total == content.size()) {
                return null;
            } else {
                allData = this.fromDbIgoValues(db.dml().find((BizObjectQueryObject) bizObjectQueryObject::getSchemaCode, bizProperties),
                        new BizPropertyMap(bizProperties));
            }
        }
        List<Map<String, Object>> list;
        if (bizObjectQueryObject.getViewType().isExistQueryCondition()) {
            // 存在查询条件 且为展示为树形时 需要轮训出 父节点
            list = queryTreeParentData(bizObjectQueryObject, bizProperties, content, parentRefFieldCode, allData);
        } else {
            list = queryTreeChildData(bizObjectQueryObject, bizProperties, content, parentRefFieldCode, depth, allData);
        }
        if (list != null) {
            list.removeAll(Collections.singletonList(null));
        }
        return list;
    }

    @Override
    public void buildTreePath(String schemaCode, List<Map<String, Object>> content, List<BizProperty> bizProperties, BizProperty bizPropertyParentRef) {
        if (CollectionUtils.isEmpty(content)) {
            return;
        }
        String parentRefFieldCode = ModelViewQueryModel.getParentRefFieldFromOption(bizPropertyParentRef.getOptions());
        List<Map<String, Object>> parentData = queryTreeParentData(() -> schemaCode, bizProperties, content, parentRefFieldCode, null);
        if (parentData == null) {
            parentData = new ArrayList<>(content.size());
        }
        parentData.addAll(content);
        Map<Object, Map<String, Object>> parentMap =
                parentData.stream().filter(it -> it.get(parentRefFieldCode) != null).collect(Collectors.toMap(k -> k.get(parentRefFieldCode), Function.identity(), (k1, k2) -> k1));
        Map<String, String> existPathMap = Maps.newHashMapWithExpectedSize(parentMap.size());
        content.forEach(it -> {
            String path = doBuildTreePath(it, bizPropertyParentRef.getRelativePropertyCode(), parentMap, existPathMap, 0);
            it.put(DefaultTreePropertyType.PATH.getCode(), path);
            existPathMap.put(String.valueOf(it.get(parentRefFieldCode)), path);
        });
    }

    private String doBuildTreePath(Map<String, Object> data, String displayFieldCode, Map<Object, Map<String, Object>> parentMap, Map<String, String> existPathMap, int depth) {

        String displayFieldValue;
        if (data.get(displayFieldCode) != null) {
            displayFieldValue = String.valueOf(data.get(displayFieldCode));
        } else {
            displayFieldValue = "";
        }
        Object parentRef = data.get(DefaultTreePropertyType.PARENT_REF.getCode());
        if (parentRef == null) {
            return displayFieldValue;
        }
        String parentRefValue = getParentRefValue(data.get(DefaultTreePropertyType.PARENT_REF.getCode()));
        String parentPathExist = existPathMap.get(parentRefValue);
        if (parentPathExist != null) {
            return parentPathExist.concat(ModelConstant.TREE_MODEL_PATH_SPILT).concat(displayFieldValue);
        }
        //防止死循环
        if (depth <= maxDepth) {
            Map<String, Object> parentData = parentMap.get(parentRefValue);
            if (parentData != null) {
                String parentPath = doBuildTreePath(parentData, displayFieldCode, parentMap, existPathMap, ++depth);
                displayFieldValue = parentPath.concat(ModelConstant.TREE_MODEL_PATH_SPILT).concat(displayFieldValue);
            }
        }
        return displayFieldValue;
    }


    /**
     * 根据父节点查询子节点数据
     * @param bizObjectQueryObject 列表查询调剂
     * @param bizProperties        数据项值
     * @param content              第一次查询的列表数据
     * @param parentRefFieldCode   所用的关联字段编码
     * @param depth                递归深度
     * @param allData              所有数据
     * @return List
     **/
    private List<Map<String, Object>> queryTreeChildData(BizObjectQueryObject bizObjectQueryObject, List<BizProperty> bizProperties,
                                                         List<Map<String, Object>> content, String parentRefFieldCode, int depth, List<Map<String, Object>> allData) {

        List<Map<String, Object>> list = new ArrayList<>();
        Set<String> parentRefFieldValues = getParentRefFieldValues(content, parentRefFieldCode);

        if (allData != null) {
            Map<String, List<Map<String, Object>>> allDataGroupBYParentRef = allData.stream().filter(it -> it.get(DefaultTreePropertyType.PARENT_REF.getCode()) != null)
                    .collect(Collectors.groupingBy(this::getParentRefFromObject));

            for (int i = 0; i < depth; i++) {
                List<Map<String, Object>> childrenList = new ArrayList<>();
                parentRefFieldValues.forEach(it -> {
                    List<Map<String, Object>> tempList = allDataGroupBYParentRef.get(it);
                    if (tempList != null) {
                        childrenList.addAll(tempList);
                    }
                });
                if (CollectionUtils.isEmpty(childrenList)) {
                    break;
                }
                list.addAll(childrenList);
                parentRefFieldValues = getParentRefFieldValues(childrenList, parentRefFieldCode);
                if (parentRefFieldValues.isEmpty()) {
                    break;
                }
            }
        } else {
            for (int i = 0; i < depth; i++) {
                List<Map<String, Object>> childrenList = queryTreeDataFromDb(bizObjectQueryObject.getSchemaCode(), bizProperties, DefaultTreePropertyType.PARENT_REF.getCode(), new ArrayList<>(parentRefFieldValues),
                        bizObjectQueryObject.getDisplayFields(), bizObjectQueryObject.getSortable());
                if (CollectionUtils.isEmpty(childrenList)) {
                    break;
                }
                list.addAll(childrenList);
                parentRefFieldValues = getParentRefFieldValues(childrenList, parentRefFieldCode);
                if (parentRefFieldValues.isEmpty()) {
                    break;
                }
            }
        }
        return list;
    }

    /**
     * 根据子节点查询父节点数据
     * @param bizObjectQueryObject 列表查询调剂
     * @param bizProperties        数据项值
     * @param content              第一次查询的列表数据
     * @param parentRefFieldCode   所用的关联字段编码
     * @param allData              所有数据
     * @return List
     **/
    private List<Map<String, Object>> queryTreeParentData(BizObjectQueryObject bizObjectQueryObject, List<BizProperty> bizProperties,
                                                          List<Map<String, Object>> content, String parentRefFieldCode, List<Map<String, Object>> allData) {
        Set<String> parentRefs = getTreeParentRefList(content);
        if (CollectionUtils.isEmpty(parentRefs)) {
            return null;
        }
        List<Map<String, Object>> list = new ArrayList<>();
        if (allData != null) {
            Map<String, Map<String, Object>> allDataGroupBYParentRefField = allData.stream().filter(it -> it.get(parentRefFieldCode) != null)
                    .collect(Collectors.toMap(k -> (String) k.get(parentRefFieldCode), Function.identity(), (k1, k2) -> k1));

            for (int i = 0; i < maxDepth; i++) {
                List<Map<String, Object>> parentList = new ArrayList<>();
                parentRefs.forEach(it -> parentList.add(allDataGroupBYParentRefField.get(it)));
                if (CollectionUtils.isEmpty(parentList)) {
                    break;
                }
                list.addAll(parentList);
                parentRefs = getTreeParentRefList(parentList);
                if (parentRefs.isEmpty()) {
                    break;
                }
            }

        } else {
            for (int i = 0; i < maxDepth; i++) {
                List<Map<String, Object>> parentList = queryTreeDataFromDb(bizObjectQueryObject.getSchemaCode(), bizProperties,
                        parentRefFieldCode, new ArrayList<>(parentRefs), bizObjectQueryObject.getDisplayFields(), bizObjectQueryObject.getSortable());
                if (CollectionUtils.isEmpty(parentList)) {
                    break;
                }
                list.addAll(parentList);
                parentRefs = getTreeParentRefList(parentList);
                if (parentRefs.isEmpty()) {
                    break;
                }
            }
        }
        return list;
    }

    private Set<String> getTreeParentRefList(List<Map<String, Object>> content) {
        return content.stream().filter(m -> m != null && m.get(DefaultTreePropertyType.PARENT_REF.getCode()) != null).map(m -> {
            Object o = m.get(DefaultTreePropertyType.PARENT_REF.getCode());
            if (o instanceof String) {
                return (String) o;
            } else {
                return JSONUtil.parseObj(o).getStr(ID);
            }
        }).collect(Collectors.toSet());
    }

    private Set<String> getParentRefFieldValues(List<Map<String, Object>> content, String parentRefFieldCode) {
        return content.stream().filter(m -> m.get(parentRefFieldCode) != null).map(m -> m.get(parentRefFieldCode).toString()).collect(Collectors.toSet());
    }

    private List<Map<String, Object>> queryTreeDataFromDb(String schemaCode, List<BizProperty> bizProperties, String propertyCode, List<String> values, List<String> displayFields, Sortable sortable) {

        List<Map<String, Object>> result = new ArrayList<>();
        Lists.partition(values, getDbQueryPerMaxNum()).forEach(ids -> {
            result.addAll(db.dml().find(new BizObjectQueryObject() {

                @Override
                public String getSchemaCode() {
                    return schemaCode;
                }

                @Override
                public FilterExpression getFilterExpression() {
                    return new FilterExpression.Item(propertyCode, FilterExpression.Op.In, ids);
                }

                @Override
                public Sortable getSortable() {
                    return sortable;
                }

                @Override
                public List<String> getDisplayFields() {
                    return displayFields;
                }

            }, bizProperties));
        });
        return fromDbIgoValues(result, new BizPropertyMap(bizProperties));
    }

    @Override
    public List<Map<String, Object>> buildTreeChildData(List<Map<String, Object>> content, String parentRefValue, String parentRefFieldCode,
                                                        boolean showPath, String displayFieldCode) {
        List<Map<String, Object>> rootData;
        Map<String, Object> parentData = null;
        if (ModelViewQueryModel.TREE_QUERY_ROOT_PARENT_REF.equals(parentRefValue) || StringUtils.isBlank(parentRefValue)) {
            rootData = content.stream().filter(it -> it.get(DefaultTreePropertyType.PARENT_REF.getCode()) == null).collect(Collectors.toList());
        } else {
            rootData = content.stream().filter(it -> it.get(DefaultTreePropertyType.PARENT_REF.getCode()) != null).filter(it -> {
                Object o = it.get(DefaultTreePropertyType.PARENT_REF.getCode());
                if (o instanceof String) {
                    return o.equals(parentRefValue);
                } else {
                    return JSONUtil.parseObj(o).getStr(parentRefFieldCode).equals(parentRefValue);
                }
            }).collect(Collectors.toList());
            parentData = content.stream().filter(it -> parentRefValue.equals(it.get(parentRefFieldCode))).findAny().orElse(null);
        }
        if (CollectionUtils.isEmpty(rootData)) {
            return Collections.emptyList();
        }
        Map<String, List<Map<String, Object>>> childrenMap = content.stream().filter(it -> it.get(DefaultTreePropertyType.PARENT_REF.getCode()) != null)
                .collect(Collectors.groupingBy(it -> {
                    Object o = it.get(DefaultTreePropertyType.PARENT_REF.getCode());
                    if (o instanceof String) {
                        return (String) o;
                    } else {
                        return JSONUtil.parseObj(o).getStr(parentRefFieldCode);
                    }
                }));

        doBuildTreeChildData(rootData, childrenMap, parentRefFieldCode, showPath, displayFieldCode, parentData);
        return rootData;
    }

    private void doBuildTreeChildData(List<Map<String, Object>> rootData, Map<String, List<Map<String, Object>>> childrenMap,
                                      String parentRefFieldCode, boolean showPath, String displayFieldCode, Map<String, Object> parentData) {
        rootData.forEach(p -> {
            if (showPath) {
                String pathDisplayValue = String.valueOf(p.get(displayFieldCode));
                if (parentData != null) {
                    pathDisplayValue = String.valueOf(parentData.get(DefaultTreePropertyType.PATH.getCode())).concat(ModelConstant.TREE_MODEL_PATH_SPILT).concat(pathDisplayValue);
                }
                p.put(DefaultTreePropertyType.PATH.getCode(), pathDisplayValue);
            }

            //修改关联字段值
            if (parentData != null) {
                Map<String, Object> parentRef = (Map<String, Object>) p.get(DefaultTreePropertyType.PARENT_REF.getCode());
                if (parentRef != null) {
                    parentRef.put(displayFieldCode, parentData.get(displayFieldCode));
                }
            }

            List<Map<String, Object>> childList = childrenMap.get((String) p.get(parentRefFieldCode));
            if (childList != null) {
                doBuildTreeChildData(childList, childrenMap, parentRefFieldCode, showPath, displayFieldCode, p);
                p.put(ModelConstant.TREE_MODEL_QUERY_CHILDREN_KEY, childList);
            }
        });
    }

//    private Map<String, Object> getObjectByPropertyCode(String schemaCode, String propertyCode, Object propertyValue, String displayCode, List<BizProperty> bizProperties) {
//        List<Map<String, Object>> list = db.dml().find(new BizObjectQueryObject() {
//            @Override
//            public String getSchemaCode() {
//                return schemaCode;
//            }
//
//            @Override
//            public FilterExpression getFilterExpression() {
//                return new FilterExpression.Item(propertyCode, FilterExpression.Op.Eq, propertyValue);
//            }
//
//            @Override
//            public List<String> getDisplayFields() {
//                return Arrays.asList(propertyCode, displayCode);
//            }
//        }, bizProperties);
//        if (CollectionUtils.isEmpty(list)) {
//            return null;
//        }
//        return list.get(0);
//    }

    @Override
    public void checkTreeCircularRef(BizObject bizObject, List<BizProperty> bizProperties) {
        String parentRefValue = getParentRefFromObject(bizObject.getData());
        if (StringUtils.isBlank(parentRefValue)) {
            return;
        }
        BizProperty parentRefProperty = getParentRefProperty(bizProperties);
        if (parentRefProperty == null) {
            return;
        }
        String parentRefFieldCode = ModelViewQueryModel.getParentRefFieldFromOption(parentRefProperty.getOptions());
        if (parentRefFieldCode.equals(DefaultTreePropertyType.PARENT_REF.getCode())) {
            return;
        }
        String targetValue = (String) bizObject.getData().get(parentRefFieldCode);
        if (parentRefValue.equals(targetValue)) {
            throw new ServiceException(ErrCode.BIZ_OBJECT_TREE_MODEL_REF_SELF.getErrCode(), ErrCode.BIZ_OBJECT_TREE_MODEL_REF_SELF.getErrMsg());
        }
        doCheckTreeCircularRef(targetValue, bizObject.getSchemaCode(), parentRefValue, null, 1);
    }

    private void doCheckTreeCircularRef(Object targetValue, String schemaCode, String parentRefValue, Map<Object, List<Map<String, Object>>> parentRefFieldMap, int depth) {
        if (depth >= maxDepth) {
            throw new ServiceException(ErrCode.BIZ_OBJECT_TREE_MODEL_DEPTH_TOO_MAX.getErrCode(), String.format(ErrCode.BIZ_OBJECT_TREE_MODEL_DEPTH_TOO_MAX.getErrMsg(), maxDepth));
        }
        Map<String, Object> perNode = null;
        // 批量插入处理，先从同批次获取父节点
        if (MapUtils.isNotEmpty(parentRefFieldMap)) {
            List<Map<String, Object>> parents = parentRefFieldMap.get(parentRefValue);
            if (CollectionUtils.isNotEmpty(parents)) {
                perNode = parents.get(0);
            }
        }
        if (perNode == null) {
            BizObject perNodeObject = bizObjectService.loadBizObject(schemaCode, (String) parentRefValue, null);
            if (perNodeObject != null) {
                perNode = perNodeObject.getData();
            }
        }
        if (perNode == null) {
            return;
        }
        parentRefValue = doCheckTreeCircularRef(perNode, targetValue);
        if (parentRefValue == null) {
            return;
        }
        doCheckTreeCircularRef(targetValue, schemaCode, parentRefValue, parentRefFieldMap, ++depth);
    }

    /**
     * 执行对比
     * @param data        待对比数据
     * @param targetValue 当前数据编码值
     * @return Object
     **/
    private String doCheckTreeCircularRef(Map<String, Object> data, Object targetValue) {
        String parentRefValue = getParentRefFromObject(data);
        if (parentRefValue != null && parentRefValue.equals(targetValue)) {
            throw new ServiceException(ErrCode.BIZ_OBJECT_TREE_MODEL_REF_CIRCULAR.getErrCode(), ErrCode.BIZ_OBJECT_TREE_MODEL_REF_CIRCULAR.getErrMsg());
        }
        return parentRefValue;
    }

    @Override
    public List<Map<String, Object>> batchCheckTreeCircularRef(List<Map<String, Object>> data, String schemaCode) {
        if (CollectionUtils.isEmpty(data)) {
            return null;
        }
        List<BizProperty> bizProperties = bizPropertyService.getListBySchemaCode(schemaCode, true);
        BizProperty parentRefProperty = getParentRefProperty(bizProperties);
        if (parentRefProperty == null) {
            return null;
        }

        String parentRefFieldCode = ModelViewQueryModel.getParentRefFieldFromOption(parentRefProperty.getOptions());
        if (parentRefFieldCode.equals(DefaultTreePropertyType.PARENT_REF.getCode())) {
            return null;
        }

        List<Map<String, Object>> bizObjectExistRef = data.stream().filter(it -> it.get(DefaultTreePropertyType.PARENT_REF.getCode()) != null).collect(Collectors.toList());
        if (CollectionUtils.isEmpty(bizObjectExistRef)) {
            return null;
        }
        List<Map<String, Object>> errorList = new ArrayList<>();
        Map<Object, List<Map<String, Object>>> parentRefFieldMap = data.stream().filter(it -> it.get(parentRefFieldCode) != null).collect(Collectors.groupingBy(it -> it.get(parentRefFieldCode)));
        parentRefFieldMap.keySet().forEach(key -> {
            if (parentRefFieldMap.get(key).size() > 1) {
                parentRefFieldMap.get(key).forEach(it -> {
                    it.put("errInfo", String.format(ErrCode.BIZ_OBJECT_TREE_MODEL_REF_NOT_UNIQUE.getErrMsg(), key));
                    errorList.add(it);
                });
            }
        });
        if (errorList.size() == data.size()) {
            return errorList;
        }
        bizObjectExistRef.forEach(it -> {
            try {
                String parentRef = getParentRefFromObject(it);
                doCheckTreeCircularRef(it.get(parentRefFieldCode), schemaCode, parentRef, parentRefFieldMap, 1);
            } catch (ServiceException e) {
                it.put("errInfo", e.getErrMsg());
                errorList.add(it);
            }
        });
        return errorList;
    }

    @Override
    public boolean checkTreeExistChild(String schemaCode, List<String> objectIds) {
        List<BizProperty> bizProperties = bizPropertyService.getListBySchemaCode(schemaCode, true);
        BizProperty parentRefProperty = getParentRefProperty(bizProperties);
        if (parentRefProperty == null) {
            return false;
        }
        String parentRefFieldCode = ModelViewQueryModel.getParentRefFieldFromOption(parentRefProperty.getOptions());
        if (parentRefFieldCode.equals(DefaultTreePropertyType.PARENT_REF.getCode())) {
            return false;
        }
        List<Map<String, Object>> dataList = db.dml().find(new BizObjectQueryObject() {
            @Override
            public String getSchemaCode() {
                return schemaCode;
            }

            @Override
            public FilterExpression getFilterExpression() {
                return new FilterExpression.Item(ID, FilterExpression.Op.In, objectIds);
            }

            @Override
            public List<String> getDisplayFields() {
                return Arrays.asList(parentRefFieldCode, DefaultTreePropertyType.PARENT_REF.getCode());
            }
        }, bizProperties);
        if (CollectionUtils.isEmpty(dataList)) {
            return false;
        }
        Set<Object> parentRefFieldCodeSet = dataList.stream().filter(it -> it.get(parentRefFieldCode) != null).map(it -> it.get(parentRefFieldCode)).collect(Collectors.toSet());
        //子节点数据
        List<Map<String, Object>> childData = db.dml().find(new BizObjectQueryObject() {

            @Override
            public String getSchemaCode() {
                return schemaCode;
            }

            @Override
            public FilterExpression getFilterExpression() {
                return new FilterExpression.Item(DefaultTreePropertyType.PARENT_REF.getCode(), FilterExpression.Op.In, parentRefFieldCodeSet);
            }

            @Override
            public List<String> getDisplayFields() {
                return Arrays.asList(parentRefFieldCode, DefaultTreePropertyType.PARENT_REF.getCode());
            }
        }, bizProperties);

        if (CollectionUtils.isEmpty(childData)) {
            return false;
        }

        Map<String, List<Map<String, Object>>> waitDelDataMap = dataList.stream().filter(it -> it.get(DefaultTreePropertyType.PARENT_REF.getCode()) != null).collect(Collectors.groupingBy(it -> String.valueOf(DefaultTreePropertyType.PARENT_REF.getCode())));
        Map<String, List<Map<String, Object>>> childDataMap = childData.stream().filter(it -> it.get(DefaultTreePropertyType.PARENT_REF.getCode()) != null).collect(Collectors.groupingBy(it -> String.valueOf(DefaultTreePropertyType.PARENT_REF.getCode())));
        // 子节点在同一删除批次时不用校验
        for (Map.Entry<String, List<Map<String, Object>>> entry : childDataMap.entrySet()) {
            String parentRef = entry.getKey();
            List<Map<String, Object>> childList = waitDelDataMap.get(parentRef);
            if (childList == null) {
                log.info("[tree model] del data check exist child. parentRef={}", parentRef);
                return true;
            }
            if (childList.size() != entry.getValue().size()) {
                log.info("[tree model] del data check exist child. parentRef={}", parentRef);
                return true;
            }
        }
        return false;
    }

    private BizProperty getParentRefProperty(List<BizProperty> bizProperties) {
        if (CollectionUtils.isEmpty(bizProperties)) {
            return null;
        }
        return bizProperties.stream().filter(it -> DefaultTreePropertyType.PARENT_REF.getCode().equals(it.getCode())).findAny().orElse(null);
    }

    private BizProperty getPathProperty(List<BizProperty> bizProperties) {
        if (CollectionUtils.isEmpty(bizProperties)) {
            return null;
        }
        return bizProperties.stream().filter(it -> DefaultTreePropertyType.PATH.getCode().equals(it.getCode())).findAny().orElse(null);
    }

    @Override
    public BizObject buildBizObjectTreePath(BizObject bizObject, List<BizProperty> bizProperties) {
        BizProperty parentRefProperty = getParentRefProperty(bizProperties);
        if (parentRefProperty == null) {
            return bizObject;
        }
        BizProperty pathProperty = getPathProperty(bizProperties);
        if (pathProperty == null) {
            return bizObject;
        }
        String parentRefFieldCode = ModelViewQueryModel.getParentRefFieldFromOption(parentRefProperty.getOptions());
        if (parentRefFieldCode.equals(DefaultTreePropertyType.PARENT_REF.getCode())) {
            return bizObject;
        }
        String treePath = buildTreePath(bizObject, bizProperties);
        bizObject.getData().put(DefaultTreePropertyType.PATH.getCode(), treePath);
        bizObject.getData().put(DefaultTreePropertyType.LEVEL.getCode(), StringUtils.countMatches(treePath, ModelConstant.TREE_MODEL_PATH_SPILT));
        return bizObject;
    }

    private String buildTreePath(BizObject bizObject, List<BizProperty> bizProperties) {
        String parentRefValue = getParentRefFromObject(bizObject.getData());
        String parentPath = getTreeParentPath(bizObject.getSchemaCode(), parentRefValue, bizProperties);
        return StringUtils.isBlank(parentPath) ? ModelConstant.TREE_MODEL_PATH_SPILT.concat(bizObject.getId()) : parentPath.concat(ModelConstant.TREE_MODEL_PATH_SPILT).concat(bizObject.getId());
    }

    private String getTreeParentPath(String schemaCode, String parentRefValue, List<BizProperty> bizProperties) {
        if (StringUtils.isNotBlank(parentRefValue)) {
            BizObject parentNodeObject = bizObjectService.loadBizObject(schemaCode, parentRefValue, null);
            if (parentNodeObject != null && parentNodeObject.getData() != null) {
                return (String) parentNodeObject.getData().get(DefaultTreePropertyType.PATH.getCode());
            }
        }
        return null;
    }

    private String getParentRefFromObject(Map<String, Object> data) {
        if (data == null) {
            return null;
        }
        Object parentRef = data.get(DefaultTreePropertyType.PARENT_REF.getCode());
        if (parentRef == null) {
            return null;
        }
        return getParentRefValue(parentRef);
    }

    private String getParentRefValue(Object value) {
        if (value instanceof String) {
            return String.valueOf(value);
        } else {
            return JSONUtil.parseObj(value).getStr(ID);
        }
    }

    @Override
    public void updateTreeChildPath(BizObject bizObject, List<BizProperty> bizProperties) {
        BizProperty parentRefProperty = getParentRefProperty(bizProperties);
        if (parentRefProperty == null) {
            return;
        }
        BizProperty pathProperty = getPathProperty(bizProperties);
        if (pathProperty == null) {
            return;
        }
        String parentRefFieldCode = ModelViewQueryModel.getParentRefFieldFromOption(parentRefProperty.getOptions());
        if (parentRefFieldCode.equals(DefaultTreePropertyType.PARENT_REF.getCode())) {
            return;
        }
        String schemaCode = bizObject.getSchemaCode();
        Optional<Map<String, Object>> dbBizObject = db.dml().findById(schemaCode, bizProperties, bizObject.getId());
        if (!dbBizObject.isPresent()) {
            return;
        }
        String pathOld = (String) dbBizObject.get().get((DefaultTreePropertyType.PATH.getCode()));
        if (StringUtils.isBlank(pathOld)) {
            log.debug("[tree model refresh path] pathOld is null, return.");
            return;
        }

        String pathNew = buildTreePath(bizObject, bizProperties);
        if (StringUtils.isBlank(pathNew)) {
            log.debug("[tree model refresh path] pathNew is null, return.");
            return;
        }
        if (pathOld.equals(pathNew)) {
            log.debug("[tree model refresh path] pathOld=pathNew, return.");
            return;
        }
        bizObject.getData().put(DefaultTreePropertyType.PATH.getCode(), pathNew);
        List<Map<String, Object>> childList = db.dml().find(new BizObjectQueryObject() {

            @Override
            public String getSchemaCode() {
                return schemaCode;
            }

            @Override
            public FilterExpression getFilterExpression() {
                return Q.it(DefaultTreePropertyType.PATH.getCode(), FilterExpression.Op.LLike, pathOld);
            }

            @Override
            public List<String> getDisplayFields() {
                return new ArrayList<>(Sets.newHashSet(ID, parentRefFieldCode, DefaultTreePropertyType.PARENT_REF.getCode(), DefaultTreePropertyType.PATH.getCode()));
            }
        }, bizProperties);
        if (CollectionUtils.isEmpty(childList)) {
            return;
        }
        childList.forEach(it -> {
            String oldPath = (String) it.get(DefaultTreePropertyType.PATH.getCode());
            if (oldPath != null) {
                String newPath = oldPath.replace(pathOld, pathNew);
                it.put(DefaultTreePropertyType.PATH.getCode(), newPath);
                it.put(DefaultTreePropertyType.LEVEL.getCode(), StringUtils.countMatches(newPath, ModelConstant.TREE_MODEL_PATH_SPILT));
                db.dml().update(schemaCode, it, ID, it.get(ID), null);
            }
        });
    }

    @Override
    public List<String> getAllTreeParentIds(String schemaCode, String objectId) {

        List<BizProperty> bizProperties = bizPropertyService.getListBySchemaCode(schemaCode, true);
        BizProperty parentRefProperty = getParentRefProperty(bizProperties);
        if (parentRefProperty == null) {
            return null;
        }
        String parentRefFieldCode = ModelViewQueryModel.getParentRefFieldFromOption(parentRefProperty.getOptions());
        if (parentRefFieldCode.equals(DefaultTreePropertyType.PARENT_REF.getCode())) {
            return null;
        }
        List<String> parentIds = new ArrayList<>();
        doGetAllTreeParents(schemaCode, objectId, parentIds, 1);
        return parentIds;
    }

    private void doGetAllTreeParents(String schemaCode, String objectId, List<String> parentIds, Integer depth) {
        if (depth >= maxDepth) {
            return;
        }
        BizObject bizObject = bizObjectService.loadBizObject(schemaCode, objectId, null);
        if (bizObject == null || bizObject.getData() == null) {
            return;
        }
        String parentRef = getParentRefFromObject(bizObject.getData());
        if (parentRef == null) {
            return;
        }
        parentIds.add(parentRef);
        doGetAllTreeParents(schemaCode, parentRef, parentIds, ++depth);
    }

    @Override
    public boolean judgeTreeParentDataAuth(BizObjectQueryObject bizObjectQueryObject, String parentRef) {

        if (bizObjectQueryObject.getAuthFilterExpression() == null || FilterExpression.empty.equals(bizObjectQueryObject.getAuthFilterExpression())) {
            return true;
        }

        List<String> allTreeParentIds = getAllTreeParentIds(bizObjectQueryObject.getSchemaCode(), parentRef);

        allTreeParentIds.add(parentRef);

        Page<Map<String, Object>> page = bizObjectService.queryBizObjects(new BizObjectQueryObject() {

            @Override
            public String getSchemaCode() {
                return bizObjectQueryObject.getSchemaCode();
            }

            @Override
            public List<String> getDisplayFields() {
                return Lists.newArrayList(ID, DefaultTreePropertyType.PARENT_REF.getCode());
            }

            @Override
            public FilterExpression getFilterExpression() {
                return Q.and(Arrays.asList(
                        Q.it(ID, FilterExpression.Op.In, allTreeParentIds),
                        bizObjectQueryObject.getAuthFilterExpression()));

            }
        });
        return page != null && CollectionUtils.isNotEmpty(page.getContent());
    }

    @Override
    public Integer getTreeTopDataLevel(BizObjectQueryObject bizObjectQueryObject, List<BizProperty> properties) {

        if (bizObjectQueryObject.getAuthFilterExpression() == null || FilterExpression.empty.equals(bizObjectQueryObject.getAuthFilterExpression())) {
            return 1;
        }
        for (int i = 1; i <= maxDepth; i++) {

            FilterExpression.And filter = Q.and(Arrays.asList(Q.it(DefaultTreePropertyType.LEVEL.getCode(), FilterExpression.Op.Eq, i),
                    bizObjectQueryObject.getAuthFilterExpression()));

            long count = db.dml().count(new BizObjectQueryObject() {

                @Override
                public String getSchemaCode() {
                    return bizObjectQueryObject.getSchemaCode();
                }

                @Override
                public List<String> getDisplayFields() {
                    return Collections.singletonList(ID);
                }

                @Override
                public FilterExpression getFilterExpression() {
                    return filter;

                }
            }, properties);

            if (count > 0) {
                return i;
            }

        }
        return 0;
    }

    @Override
    public Pair<String, FilterExpression> getTreeTopDataPathFilter(BizObjectQueryObject bizObjectQueryObject, Integer level) {

        if (bizObjectQueryObject.getAuthFilterExpression() == null || FilterExpression.empty.equals(bizObjectQueryObject.getAuthFilterExpression())) {
            return null;
        }

        Page<Map<String, Object>> page = bizObjectService.queryBizObjects(new BizObjectQueryObject() {

            @Override
            public String getSchemaCode() {
                return bizObjectQueryObject.getSchemaCode();
            }

            @Override
            public List<String> getDisplayFields() {
                return Lists.newArrayList(ID, DefaultTreePropertyType.PARENT_REF.getCode(), DefaultTreePropertyType.PATH.getCode());
            }

            @Override
            public FilterExpression getFilterExpression() {
                return Q.and(Arrays.asList(
                        Q.it(DefaultTreePropertyType.LEVEL.getCode(), FilterExpression.Op.Eq, level),
                        bizObjectQueryObject.getAuthFilterExpression()));
            }
        });
        if (page == null || CollectionUtils.isEmpty(page.getContent())) {
            return null;
        }
        List<FilterExpression> filterExpressions = Lists.newArrayListWithExpectedSize(page.getContent().size());
        page.getContent().stream().filter(it -> it.get(DefaultTreePropertyType.PATH.getCode()) != null)
                .forEach(it -> filterExpressions.add(Q.it(DefaultTreePropertyType.PATH.getCode(), FilterExpression.Op.LLike, it.get(DefaultTreePropertyType.PATH.getCode()))));
        if (filterExpressions.isEmpty()) {
            return null;
        }
        FilterExpression filterExpression = filterExpressions.size() == 1 ? filterExpressions.get(0) : Q.or(filterExpressions);
        return Pair.of(getParentRefFromObject(page.getContent().get(0)), filterExpression);
    }

    @Override
    public BizObject executeCalculateRule(BizObject bizObject) {
        return executeCalculateRule(bizObject, false);
    }

    @Override
    public BizObject executeCalculateRule(BizObject bizObject, boolean executeCalRule) {
        if (bizObject == null || StringUtils.isBlank(bizObject.getSchemaCode()) || MapUtils.isEmpty(bizObject.getData())) {
            return bizObject;
        }
        String schemaCode = bizObject.getSchemaCode();
        List<BizDataRuleModel> calDataRules = bizDataRuleFacade.getListBySchemaCodeAndDataRuleType(schemaCode, DataRuleType.CALCULATE_RULE);
        if (CollectionUtils.isEmpty(calDataRules)) {
            return bizObject;
        }
        //  过滤格式不正确的计算规则
        calDataRules.stream().filter(it -> bizDataRuleFacade.doCheckDataRuleFormat(it) == null).forEach(it -> {

            if (!it.isChildRule()) {
                // 主表规则
                if (!executeCalRule && bizObject.getData().get(it.getPropertyCode()) != null) {
                    return;
                }
                String expression = bizDataRuleFacade.getCalRuleExpression(it.getOptions());
                Set<String> calDataRuleParams = bizDataRuleFacade.getCalDataRuleParams(expression, true);
                Map<String, Object> paramsMap = Maps.newHashMapWithExpectedSize(calDataRuleParams.size());
                calDataRuleParams.forEach(p -> {
                    if (p.contains(".")) {
                        String[] split = p.split("\\.");
                        List<Object> sheetData = getChildDataForCalRule(bizObject, split[0], split[1]);
                        if (sheetData != null) {
                            paramsMap.put(p, sheetData);
                        }
                    } else {
                        paramsMap.put(p, bizObject.getData().get(p));
                    }
                });
                executeCalculateRule(bizObject.getData(), it.getPropertyCode(), bizObject.getSchemaCode(), expression, paramsMap);
            } else {
                //子表规则
                Object sheetObject = bizObject.getData().get(it.getSchemaCode());
                if (sheetObject == null) {
                    return;
                }
                List<Map<String, Object>> sheetDataList = (List<Map<String, Object>>) sheetObject;
                String expression = bizDataRuleFacade.getCalRuleExpression(it.getOptions());
                Set<String> calDataRuleParams = bizDataRuleFacade.getCalDataRuleParams(expression, true);
                Map<String, Object> paramsMap = Maps.newHashMapWithExpectedSize(calDataRuleParams.size());
                calDataRuleParams.stream().filter(param -> !param.contains(".")).forEach(param -> {
                    paramsMap.put(param, bizObject.getData().get(param));
                });
                //子表 聚合函数使用子表字段时处理，数据使用所有子表字段值？ TODO
                sheetDataList.forEach(s -> {
                    if (s == null) {
                        return;
                    }
                    if (!executeCalRule && s.get(it.getPropertyCode()) != null) {
                        return;
                    }
                    calDataRuleParams.stream().filter(param -> param.contains(".")).forEach(param -> {
                        paramsMap.put(param, s.get(param.substring(param.indexOf('.') + 1)));
                    });
                    executeCalculateRule(s, it.getPropertyCode(), it.getSchemaCode(), expression, paramsMap);
                });
            }

        });
        return bizObject;
    }

    public void executeCalculateRule(Map<String, Object> data, String propertyCode, String schemaCode, String expression, Map<String, Object> paramsMap) {
        if (MapUtils.isEmpty(paramsMap)) {
            log.debug("[execute calculate rule] paramsMap is null, propertyCode={} expression={} paramsMap={}", propertyCode, expression, paramsMap);
            return;
        }
        if (paramsMap.values().stream().anyMatch(Objects::isNull)) {
            log.debug("[execute calculate rule] paramsMap exist null, propertyCode={} expression={} paramsMap={}", propertyCode, expression, paramsMap);
            return;
        }
        try {
            Object o = bizDataRuleFacade.calculateRule(expression, schemaCode, paramsMap);
            if (o == null) {
                log.debug("[execute calculate rule] result is null, propertyCode={} expression={} paramsMap={}", propertyCode, expression, paramsMap);
                return;
            }
            data.put(propertyCode, o);
        } catch (Exception e) {
            log.error("[execute calculate rule] calculate error, propertyCode={} expression={} paramsMap={}", propertyCode, expression, paramsMap);
//            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
        }
    }

    private List<Object> getChildDataForCalRule(BizObject bizObject, String schemaCode, String propertyCode) {
        if (bizObject == null) {
            return null;
        }
        Object sheetDataObject = bizObject.getData().get(schemaCode);
        if (sheetDataObject == null) {
            return null;
        }
        List<Map<String, Object>> sheetData = (List<Map<String, Object>>) sheetDataObject;
        if (CollectionUtils.isEmpty(sheetData)) {
            return null;
        }
        return sheetData.stream().filter(it -> it.get(propertyCode) != null)
                .map(it -> it.get(propertyCode)).collect(Collectors.toList());
    }

    @Override
    public List<Object> margeChildDataFromDb(BizObjectModel bizObjectModel, String subSchemaCode, String propertyCode) {

        List<Map<String, Object>> childDataDb = this.findChildTableData(subSchemaCode, new BizObjectQueryObject() {
            @Override
            public String getSchemaCode() {
                return subSchemaCode;
            }

            @Override
            public FilterExpression getFilterExpression() {
                return Q.it(DefaultSubPropertyType.PARENT_ID.getCode(), FilterExpression.Op.Eq, bizObjectModel.getId());
            }

            @Override
            public List<String> getDisplayFields() {
                Set<String> displayFields = Sets.newHashSet(DefaultPropertyType.ID.getCode(), propertyCode);
                return new ArrayList<>(displayFields);
            }
        });
        List<Map<String, Object>> childDataInput = null;
        Map<String, Object> data = bizObjectModel.getData();
        if (data != null) {
            childDataInput = (List<Map<String, Object>>) data.get(subSchemaCode);
        }
        boolean inputDataIsNull = CollectionUtils.isEmpty(childDataInput);
        boolean dbDataIsNull = CollectionUtils.isEmpty(childDataDb);
        if (inputDataIsNull && dbDataIsNull) {
            return null;
        } else if (inputDataIsNull) {
            return childDataDb.stream().map(it -> it.get(propertyCode)).collect(Collectors.toList());
        } else if (dbDataIsNull) {
            return childDataInput.stream().map(it -> it.get(propertyCode)).collect(Collectors.toList());
        } else {
            Map<String, List<Map<String, Object>>> childDataInputMap = childDataInput.stream().collect(Collectors.groupingBy(it -> {
                Object rowStatus = it.get(BizObject.DATAROWSTATUS);
                if (rowStatus != null) {
                    if (rowStatus instanceof DataRowStatus) {
                        return ((DataRowStatus) rowStatus).name();
                    }
                    return (String) rowStatus;
                }
                if (it.get(DefaultPropertyType.ID.getCode()) == null) {
                    return DataRowStatus.Added.name();
                } else {
                    return DataRowStatus.Modified.name();
                }
            }));

            List<Map<String, Object>> update = childDataInputMap.get(DataRowStatus.Modified.name());
            List<Map<String, Object>> delete = childDataInputMap.get(DataRowStatus.Deleted.name());
            Map<String, Map<String, Object>> childDataDbMap = childDataDb.stream().collect(Collectors.toMap(k -> (String) k.get(DefaultPropertyType.ID.getCode()), Function.identity(), (k1, k2) -> k1));
            if (update != null) {
                Map<String, Map<String, Object>> updateMap = update.stream().collect(Collectors.toMap(k -> (String) k.get(DefaultPropertyType.ID.getCode()), Function.identity(), (k1, k2) -> k1));
                childDataDbMap.putAll(updateMap);
            }
            if (delete != null) {
                Set<String> deleteIds = delete.stream().map(it -> (String) it.get(DefaultPropertyType.ID.getCode())).collect(Collectors.toSet());
                deleteIds.forEach(childDataDbMap::remove);
            }
            List<Object> result = childDataDbMap.values().stream().map(it -> it.get(propertyCode)).collect(Collectors.toList());
            List<Map<String, Object>> add = childDataInputMap.get(DataRowStatus.Added.name());
            if (add != null) {
                result.addAll(add.stream().map(it -> it.get(propertyCode)).collect(Collectors.toList()));
            }
            result.removeIf(Objects::isNull);
            return result;
        }

    }

    @Override
    public List<String> completeSystemFields(String schemaCode, Map<String, Object> quotes, List<String> displayFields) {
        HashSet<String> displayFieldSet = Sets.newHashSet(DefaultPropertyType.ID.getCode(),
                DefaultPropertyType.NAME.getCode(),
                DefaultPropertyType.OWNER.getCode(),
                DefaultPropertyType.CREATER.getCode(),
                DefaultPropertyType.CREATED_TIME.getCode(),
                DefaultPropertyType.MODIFIED_TIME.getCode(),
                DefaultPropertyType.SEQUENCE_STATUS.getCode(),
                DefaultPropertyType.WORKFLOW_INSTANCE_ID.getCode());
        displayFieldSet.addAll(displayFields);

        //引用类型的关联表单的数据  必须返回
        if (MapUtils.isNotEmpty(quotes)) {
            Object o = quotes.get(schemaCode);
            if (Objects.nonNull(o)) {
                Map<String, List<QuoteModel>> map = (Map<String, List<QuoteModel>>) o;
                Set<String> workSheets = map.keySet();
                if (!workSheets.isEmpty()) {
                    displayFieldSet.addAll(workSheets);
                }
            }
        }
        return new ArrayList<>(displayFieldSet);
    }


    public List<String> completeChildSystemFields(String schemaCode, Map<String, Object> quotes, List<String> displayFields) {
        HashSet<String> displayFieldSet = Sets.newHashSet(DefaultPropertyType.ID.getCode(),
                DefaultSubPropertyType.ID.getCode(),
                DefaultSubPropertyType.PARENT_ID.getCode());
        displayFieldSet.addAll(displayFields);

        //引用类型的关联表单的数据  必须返回
        if (MapUtils.isNotEmpty(quotes)) {
            Object o = quotes.get(schemaCode);
            if (Objects.nonNull(o)) {
                Map<String, List<QuoteModel>> map = (Map<String, List<QuoteModel>>) o;
                Set<String> workSheets = map.keySet();
                if (!workSheets.isEmpty()) {
                    displayFieldSet.addAll(workSheets);
                }
            }
        }
        return new ArrayList<>(displayFieldSet);
    }

}

